Add giflib and GifDrawable

This commit is contained in:
DrKLO 2014-03-27 18:25:53 +04:00
parent 22918f143a
commit 5323e87ba3
44 changed files with 3472 additions and 871 deletions

View File

@ -154,6 +154,18 @@ LOCAL_SRC_FILES += \
./opus/opusfile/opusfile.c \
./opus/opusfile/stream.c
LOCAL_SRC_FILES += \
./giflib/dgif_lib.c \
./giflib/gifalloc.c
LOCAL_SRC_FILES += \
./aes/aes_core.c \
./aes/aes_ige.c \
./aes/aes_misc.c
LOCAL_SRC_FILES += \
./sqlite/sqlite3.c
LOCAL_C_INCLUDES := \
./opus/include \
./opus/silk \
@ -163,16 +175,12 @@ LOCAL_C_INCLUDES := \
./opus/opusfile
LOCAL_SRC_FILES += \
./aes_core.c \
./aes_ige.c \
./aes_misc.c \
./jni.c \
./sqlite3.c \
./org_telegram_SQLite_SQLiteCursor.c \
./org_telegram_SQLite_SQLiteDatabase.c \
./org_telegram_SQLite_SQLitePreparedStatement.c \
./org_telegram_SQLite.c \
./audio.c
./sqlite_cursor.c \
./sqlite_database.c \
./sqlite_statement.c \
./sqlite.c \
./audio.c \
./gif.c
include $(BUILD_SHARED_LIBRARY)

View File

@ -5,14 +5,7 @@
#include <stdlib.h>
#include <time.h>
#include <opusfile.h>
#include "log.h"
#ifndef max
#define max(x, y) ((x) > (y)) ? (x) : (y)
#endif
#ifndef min
#define min(x, y) ((x) < (y)) ? (x) : (y)
#endif
#include "utils.h"
typedef struct {
int version;
@ -540,9 +533,6 @@ int64_t _currentPcmOffset = 0;
int _finished = 0;
static const int playerBuffersCount = 3;
static const int playerSampleRate = 48000;
int finished;
int pcmOffset;
int size;
void cleanupPlayer() {
if (_opusFile) {
@ -585,14 +575,14 @@ int initPlayer(const char *path) {
return 1;
}
void fillBuffer(uint8_t *buffer, int capacity) {
void fillBuffer(uint8_t *buffer, int capacity, int *args) {
if (_opusFile) {
pcmOffset = max(0, op_pcm_tell(_opusFile));
args[1] = max(0, op_pcm_tell(_opusFile));
if (_finished) {
finished = 1;
size = 0;
pcmOffset = 0;
args[0] = 0;
args[1] = 0;
args[2] = 1;
return;
} else {
int writtenOutputBytes = 0;
@ -612,19 +602,19 @@ void fillBuffer(uint8_t *buffer, int capacity) {
}
}
size = writtenOutputBytes;
args[0] = writtenOutputBytes;
if (endOfFileReached || pcmOffset + size == _totalPcmDuration) {
if (endOfFileReached || args[1] + args[0] == _totalPcmDuration) {
_finished = 1;
finished = 1;
args[2] = 1;
} else {
finished = 0;
args[2] = 0;
}
}
} else {
memset(buffer, 0, capacity);
size = capacity;
pcmOffset = _totalPcmDuration;
args[0] = capacity;
args[1] = _totalPcmDuration;
}
}
@ -632,21 +622,11 @@ JNIEXPORT jlong Java_org_telegram_messenger_MediaController_getTotalPcmDuration(
return _totalPcmDuration;
}
JNIEXPORT int Java_org_telegram_messenger_MediaController_getFinished(JNIEnv *env, jclass class) {
return finished;
}
JNIEXPORT int Java_org_telegram_messenger_MediaController_getSize(JNIEnv *env, jclass class) {
return size;
}
JNIEXPORT jlong Java_org_telegram_messenger_MediaController_getPcmOffset(JNIEnv *env, jclass class) {
return pcmOffset;
}
JNIEXPORT void Java_org_telegram_messenger_MediaController_readOpusFile(JNIEnv *env, jclass class, jobject buffer, jint capacity) {
JNIEXPORT void Java_org_telegram_messenger_MediaController_readOpusFile(JNIEnv *env, jclass class, jobject buffer, jint capacity, jintArray args) {
jint *argsArr = (*env)->GetIntArrayElements(env, args, 0);
jbyte *bufferBytes = (*env)->GetDirectBufferAddress(env, buffer);
fillBuffer(bufferBytes, capacity);
fillBuffer(bufferBytes, capacity, argsArr);
(*env)->ReleaseIntArrayElements(env, args, argsArr, 0);
}
JNIEXPORT int Java_org_telegram_messenger_MediaController_seekOpusFile(JNIEnv *env, jclass class, jfloat position) {

View File

@ -1,128 +0,0 @@
#!/bin/bash
function build_one {
echo "Cleaning..."
make clean
echo "Configuring..."
./configure --target-os=linux \
--prefix=$PREFIX \
--enable-cross-compile \
--extra-libs="-lgcc" \
--arch=$ARCH \
--cc=$CC \
--cross-prefix=$CROSS_PREFIX \
--nm=$NM \
--sysroot=$PLATFORM \
--extra-cflags=" -O3 -fpic -DANDROID -DHAVE_SYS_UIO_H=1 -fasm -Wno-psabi -fno-short-enums -Dipv6mr_interface=ipv6mr_ifindex -fno-strict-aliasing -finline-limit=300 $OPTIMIZE_CFLAGS " \
--disable-shared \
--enable-static \
--extra-ldflags="-Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib -lc -lm -ldl" \
\
--disable-everything \
--disable-network \
--enable-small \
--enable-zlib \
--disable-avfilter \
--disable-avdevice \
--disable-programs \
--disable-doc \
--disable-lsp \
--disable-dwt \
--disable-dct \
--enable-stripping \
--disable-postproc \
--disable-fft \
--disable-lzo \
--disable-rdft \
--disable-mdct \
--disable-debug \
\
--enable-muxer='mp4' \
--enable-protocol='file' \
--enable-encoder='aac,mpeg4' \
--enable-decoder='aac,amrnb,amrwb,flv,h263,h264' \
--enable-demuxer='flv,mpegvideo,mov' \
--enable-hwaccel='mpeg4_vaapi,mpeg4_vdpau' \
--enable-swresample \
--enable-swscale \
--enable-asm \
$ADDITIONAL_CONFIGURE_FLAG
echo "continue?"
read
make -j8 install
#$AR d libavcodec/libavcodec.a inverse.o
#$LD -rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -soname libffmpeg.a -static -nostdlib -z noexecstack -Bsymbolic --whole-archive --no-undefined -o $PREFIX/libffmpeg.so libavcodec/libavcodec.a libavformat/libavformat.a libavutil/libavutil.a -lc -lm -lz -ldl --dynamic-linker=/system/bin/linker $GCCLIB
}
NDK=/Users/DrKLO/ndk9
#arm platform
PLATFORM=$NDK/platforms/android-8/arch-arm
PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64
LD=$PREBUILT/bin/arm-linux-androideabi-ld
AR=$PREBUILT/bin/arm-linux-androideabi-ar
NM=$PREBUILT/bin/arm-linux-androideabi-nm
GCCLIB=$PREBUILT/lib/gcc/arm-linux-androideabi/4.8/libgcc.a
ARCH=arm
CC=$PREBUILT/bin/arm-linux-androideabi-gcc
CROSS_PREFIX=$PREBUILT/bin/arm-linux-androideabi-
#arm v6
CPU=armv6
OPTIMIZE_CFLAGS="-marm -march=$CPU"
PREFIX=/Users/DrKLO/ndk9/platforms/android-9/arch-$ARCH/usr
ADDITIONAL_CONFIGURE_FLAG=
build_one
#arm v7vfpv3
#CPU=armv7-a
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfpv3-d16 -marm -march=$CPU "
#PREFIX=./android/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#arm v7vfp
#CPU=armv7-a
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
#PREFIX=./android/$CPU-vfp
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#arm v7n
#CPU=armv7-a
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=neon -marm -march=$CPU -mtune=cortex-a8"
#PREFIX=./android/$CPU
#ADDITIONAL_CONFIGURE_FLAG=--enable-neon
#build_one
#arm v6+vfp
#CPU=armv6
#OPTIMIZE_CFLAGS="-DCMP_HAVE_VFP -mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU"
#PREFIX=./android/${CPU}_vfp
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#x86 platform
PLATFORM=$NDK/platforms/android-9/arch-x86
PREBUILT=$NDK/toolchains/x86-4.8/prebuilt/darwin-x86_64
LD=$PREBUILT/bin/i686-linux-android-ld
AR=$PREBUILT/bin/i686-linux-android-ar
NM=$PREBUILT/bin/i686-linux-android-nm
GCCLIB=$PREBUILT/lib/gcc/i686-linux-android/4.8/libgcc.a
ARCH=x86
CC=$PREBUILT/bin/i686-linux-android-gcc
CROSS_PREFIX=$PREBUILT/bin/i686-linux-android-
CPU=i686
OPTIMIZE_CFLAGS="-march=$CPU"
PREFIX=/Users/DrKLO/ndk9/platforms/android-9/arch-$ARCH/usr
ADDITIONAL_CONFIGURE_FLAG="--disable-mmx --disable-yasm"
build_one

824
TMessagesProj/jni/gif.c Normal file
View File

@ -0,0 +1,824 @@
//tanks to https://github.com/koral--/android-gif-drawable
/*
MIT License
Copyright (c)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
// Copyright (c) 2011 Google Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond
*/
#include <jni.h>
#include <stdio.h>
#include <time.h>
#include <limits.h>
#include "gif.h"
#include "giflib/gif_lib.h"
#define D_GIF_ERR_NO_FRAMES 1000
#define D_GIF_ERR_INVALID_SCR_DIMS 1001
#define D_GIF_ERR_INVALID_IMG_DIMS 1002
#define D_GIF_ERR_IMG_NOT_CONFINED 1003
typedef struct {
uint8_t blue;
uint8_t green;
uint8_t red;
uint8_t alpha;
} argb;
typedef struct {
unsigned int duration;
short transpIndex;
unsigned char disposalMethod;
} FrameInfo;
typedef struct {
GifFileType *gifFilePtr;
unsigned long lastFrameReaminder;
unsigned long nextStartTime;
int currentIndex;
unsigned int lastDrawIndex;
FrameInfo *infos;
argb *backupPtr;
int startPos;
unsigned char *rasterBits;
char *comment;
unsigned short loopCount;
int currentLoop;
jfloat speedFactor;
} GifInfo;
static ColorMapObject *defaultCmap = NULL;
static ColorMapObject *genDefColorMap(void) {
ColorMapObject *cmap = GifMakeMapObject(256, NULL);
if (cmap != NULL) {
int iColor;
for (iColor = 0; iColor < 256; iColor++) {
cmap->Colors[iColor].Red = (GifByteType) iColor;
cmap->Colors[iColor].Green = (GifByteType) iColor;
cmap->Colors[iColor].Blue = (GifByteType) iColor;
}
}
return cmap;
}
jint gifOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env) {
defaultCmap = genDefColorMap();
if (defaultCmap == NULL) {
return -1;
}
return JNI_VERSION_1_6;
}
void gifOnJNIUnload(JavaVM *vm, void *reserved) {
GifFreeMapObject(defaultCmap);
}
static int fileReadFunc(GifFileType *gif, GifByteType *bytes, int size) {
FILE *file = (FILE *)gif->UserData;
return fread(bytes, 1, size, file);
}
static int fileRewindFun(GifInfo *info) {
return fseek(info->gifFilePtr->UserData, info->startPos, SEEK_SET);
}
static unsigned long getRealTime() {
struct timespec ts;
const clockid_t id = CLOCK_MONOTONIC;
if (id != (clockid_t) - 1 && clock_gettime(id, &ts) != -1)
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
return -1;
}
static void cleanUp(GifInfo *info) {
if (info->backupPtr) {
free(info->backupPtr);
info->backupPtr = NULL;
}
if (info->infos) {
free(info->infos);
info->infos = NULL;
}
if (info->rasterBits) {
free(info->rasterBits);
info->rasterBits = NULL;
}
if (info->comment) {
free(info->comment);
info->comment = NULL;
}
GifFileType *GifFile = info->gifFilePtr;
if (GifFile->SColorMap == defaultCmap) {
GifFile->SColorMap = NULL;
}
if (GifFile->SavedImages != NULL) {
SavedImage *sp;
for (sp = GifFile->SavedImages; sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
if (sp->ImageDesc.ColorMap != NULL) {
GifFreeMapObject(sp->ImageDesc.ColorMap);
sp->ImageDesc.ColorMap = NULL;
}
}
free(GifFile->SavedImages);
GifFile->SavedImages = NULL;
}
DGifCloseFile(GifFile);
free(info);
}
static int getComment(GifByteType *Bytes, char **cmt) {
unsigned int len = (unsigned int) Bytes[0];
unsigned int offset = *cmt != NULL ? strlen(*cmt) : 0;
char *ret = realloc(*cmt, (len + offset + 1) * sizeof(char));
if (ret != NULL) {
memcpy(ret + offset, &Bytes[1], len);
ret[len + offset] = 0;
*cmt = ret;
return GIF_OK;
}
return GIF_ERROR;
}
static void packARGB32(argb *pixel, GifByteType alpha, GifByteType red, GifByteType green, GifByteType blue) {
pixel->alpha = alpha;
pixel->red = red;
pixel->green = green;
pixel->blue = blue;
}
static void getColorFromTable(int idx, argb *dst, const ColorMapObject *cmap) {
char colIdx = idx >= cmap->ColorCount ? 0 : idx;
GifColorType *col = &cmap->Colors[colIdx];
packARGB32(dst, 0xFF, col->Red, col->Green, col->Blue);
}
static void eraseColor(argb *bm, int w, int h, argb color) {
int i;
for (i = 0; i < w * h; i++) {
*(bm + i) = color;
}
}
static inline bool setupBackupBmp(GifInfo *info, short transpIndex) {
GifFileType *fGIF = info->gifFilePtr;
info->backupPtr = calloc(fGIF->SWidth * fGIF->SHeight, sizeof(argb));
if (!info->backupPtr) {
info->gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
return false;
}
argb paintingColor;
if (transpIndex == -1) {
getColorFromTable(fGIF->SBackGroundColor, &paintingColor, fGIF->SColorMap);
} else {
packARGB32(&paintingColor,0,0,0,0);
}
eraseColor(info->backupPtr, fGIF->SWidth, fGIF->SHeight, paintingColor);
return true;
}
static int readExtensions(int ExtFunction, GifByteType *ExtData, GifInfo *info) {
if (ExtData == NULL) {
return GIF_OK;
}
if (ExtFunction == GRAPHICS_EXT_FUNC_CODE && ExtData[0] == 4) {
FrameInfo *fi = &info->infos[info->gifFilePtr->ImageCount];
fi->transpIndex = -1;
char *b = (char *)ExtData + 1;
short delay = ((b[2] << 8) | b[1]);
fi->duration = delay > 1 ? delay * 10 : 100;
fi->disposalMethod = ((b[0] >> 2) & 7);
if (ExtData[1] & 1)
fi->transpIndex = (short) b[3];
if (fi->disposalMethod == 3 && info->backupPtr == NULL) {
if (!setupBackupBmp(info,fi->transpIndex)) {
return GIF_ERROR;
}
}
} else if (ExtFunction == COMMENT_EXT_FUNC_CODE) {
if (getComment(ExtData, &info->comment) == GIF_ERROR) {
info->gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
return GIF_ERROR;
}
} else if (ExtFunction == APPLICATION_EXT_FUNC_CODE && ExtData[0] == 11) {
if (strncmp("NETSCAPE2.0", &ExtData[1], 11) == 0 || strncmp("ANIMEXTS1.0", &ExtData[1], 11) == 0) {
if (DGifGetExtensionNext(info->gifFilePtr, &ExtData, &ExtFunction) == GIF_ERROR) {
return GIF_ERROR;
}
if (ExtFunction == APPLICATION_EXT_FUNC_CODE && ExtData[0] == 3 && ExtData[1] == 1) {
info->loopCount = (unsigned short) (ExtData[2] + (ExtData[3] << 8));
}
}
}
return GIF_OK;
}
static int DDGifSlurp(GifFileType *GifFile, GifInfo *info, bool shouldDecode) {
GifRecordType RecordType;
GifByteType *ExtData;
int codeSize;
int ExtFunction;
size_t ImageSize;
do {
if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
return GIF_ERROR;
}
switch (RecordType) {
case IMAGE_DESC_RECORD_TYPE: {
if (DGifGetImageDesc(GifFile, !shouldDecode) == GIF_ERROR) {
return GIF_ERROR;
}
int i = shouldDecode ? info->currentIndex : GifFile->ImageCount - 1;
SavedImage *sp = &GifFile->SavedImages[i];
ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;
if (sp->ImageDesc.Width < 1 || sp->ImageDesc.Height < 1 || ImageSize > (SIZE_MAX / sizeof(GifPixelType))) {
GifFile->Error = D_GIF_ERR_INVALID_IMG_DIMS;
return GIF_ERROR;
}
if (sp->ImageDesc.Width > GifFile->SWidth || sp->ImageDesc.Height > GifFile->SHeight) {
GifFile->Error = D_GIF_ERR_IMG_NOT_CONFINED;
return GIF_ERROR;
}
if (shouldDecode) {
sp->RasterBits = info->rasterBits;
if (sp->ImageDesc.Interlace) {
int i, j;
int InterlacedOffset[] = { 0, 4, 2, 1 };
int InterlacedJumps[] = { 8, 8, 4, 2 };
for (i = 0; i < 4; i++) {
for (j = InterlacedOffset[i]; j < sp->ImageDesc.Height; j += InterlacedJumps[i]) {
if (DGifGetLine(GifFile, sp->RasterBits + j * sp->ImageDesc.Width, sp->ImageDesc.Width) == GIF_ERROR) {
return GIF_ERROR;
}
}
}
} else {
if (DGifGetLine(GifFile, sp->RasterBits, ImageSize) == GIF_ERROR) {
return GIF_ERROR;
}
}
if (info->currentIndex >= GifFile->ImageCount - 1) {
if (info->loopCount > 0)
info->currentLoop++;
if (fileRewindFun(info) != 0) {
info->gifFilePtr->Error = D_GIF_ERR_READ_FAILED;
return GIF_ERROR;
}
}
return GIF_OK;
} else {
if (DGifGetCode(GifFile, &codeSize, &ExtData) == GIF_ERROR) {
return GIF_ERROR;
}
while (ExtData) {
if (DGifGetCodeNext(GifFile, &ExtData) == GIF_ERROR) {
return GIF_ERROR;
}
}
}
break;
}
case EXTENSION_RECORD_TYPE: {
if (DGifGetExtension(GifFile, &ExtFunction, &ExtData) == GIF_ERROR) {
return GIF_ERROR;
}
if (!shouldDecode) {
info->infos = realloc(info->infos, (GifFile->ImageCount + 1) * sizeof(FrameInfo));
if (readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) {
return GIF_ERROR;
}
}
while (ExtData) {
if (DGifGetExtensionNext(GifFile, &ExtData, &ExtFunction) == GIF_ERROR) {
return GIF_ERROR;
}
if (!shouldDecode) {
if (readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) {
return GIF_ERROR;
}
}
}
break;
}
case TERMINATE_RECORD_TYPE:
default:
break;
}
} while (RecordType != TERMINATE_RECORD_TYPE);
bool ok = true;
if (shouldDecode) {
ok = (fileRewindFun(info) == 0);
}
if (ok) {
return GIF_OK;
} else {
info->gifFilePtr->Error = D_GIF_ERR_READ_FAILED;
return GIF_ERROR;
}
}
static void copyLine(argb *dst, const unsigned char *src, const ColorMapObject *cmap, int transparent, int width) {
for (; width > 0; width--, src++, dst++) {
if (*src != transparent) {
getColorFromTable(*src, dst, cmap);
}
}
}
static argb *getAddr(argb *bm, int width, int left, int top) {
return bm + top * width + left;
}
static void blitNormal(argb *bm, int width, int height, const SavedImage *frame, const ColorMapObject *cmap, int transparent) {
const unsigned char *src = (unsigned char *)frame->RasterBits;
argb *dst = getAddr(bm, width, frame->ImageDesc.Left, frame->ImageDesc.Top);
GifWord copyWidth = frame->ImageDesc.Width;
if (frame->ImageDesc.Left + copyWidth > width) {
copyWidth = width - frame->ImageDesc.Left;
}
GifWord copyHeight = frame->ImageDesc.Height;
if (frame->ImageDesc.Top + copyHeight > height) {
copyHeight = height - frame->ImageDesc.Top;
}
int srcPad, dstPad;
dstPad = width - copyWidth;
srcPad = frame->ImageDesc.Width - copyWidth;
for (; copyHeight > 0; copyHeight--) {
copyLine(dst, src, cmap, transparent, copyWidth);
src += frame->ImageDesc.Width;
dst += width;
}
}
static void fillRect(argb *bm, int bmWidth, int bmHeight, GifWord left, GifWord top, GifWord width, GifWord height, argb col) {
uint32_t *dst = (uint32_t *)getAddr(bm, bmWidth, left, top);
GifWord copyWidth = width;
if (left + copyWidth > bmWidth) {
copyWidth = bmWidth - left;
}
GifWord copyHeight = height;
if (top + copyHeight > bmHeight) {
copyHeight = bmHeight - top;
}
uint32_t *pColor = (uint32_t *)(&col);
for (; copyHeight > 0; copyHeight--) {
memset(dst, *pColor, copyWidth * sizeof(argb));
dst += bmWidth;
}
}
static void drawFrame(argb *bm, int bmWidth, int bmHeight, const SavedImage *frame, const ColorMapObject *cmap, short transpIndex) {
if (frame->ImageDesc.ColorMap != NULL) {
cmap = frame->ImageDesc.ColorMap;
if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
cmap = defaultCmap;
}
}
blitNormal(bm, bmWidth, bmHeight, frame, cmap, (int) transpIndex);
}
static bool checkIfCover(const SavedImage *target, const SavedImage *covered) {
if (target->ImageDesc.Left <= covered->ImageDesc.Left
&& covered->ImageDesc.Left + covered->ImageDesc.Width
<= target->ImageDesc.Left + target->ImageDesc.Width
&& target->ImageDesc.Top <= covered->ImageDesc.Top
&& covered->ImageDesc.Top + covered->ImageDesc.Height
<= target->ImageDesc.Top + target->ImageDesc.Height) {
return true;
}
return false;
}
static inline void disposeFrameIfNeeded(argb *bm, GifInfo *info, unsigned int idx) {
argb *backup = info->backupPtr;
argb color;
packARGB32(&color, 0, 0, 0, 0);
GifFileType *fGif = info->gifFilePtr;
SavedImage *cur = &fGif->SavedImages[idx - 1];
SavedImage *next = &fGif->SavedImages[idx];
bool curTrans = info->infos[idx - 1].transpIndex != -1;
int curDisposal = info->infos[idx - 1].disposalMethod;
bool nextTrans = info->infos[idx].transpIndex != -1;
int nextDisposal = info->infos[idx].disposalMethod;
argb *tmp;
if ((curDisposal == 2 || curDisposal == 3) && (nextTrans || !checkIfCover(next, cur))) {
switch (curDisposal) {
case 2: {
fillRect(bm, fGif->SWidth, fGif->SHeight, cur->ImageDesc.Left, cur->ImageDesc.Top, cur->ImageDesc.Width, cur->ImageDesc.Height, color);
}
break;
case 3: {
tmp = bm;
bm = backup;
backup = tmp;
}
break;
}
}
if (nextDisposal == 3) {
memcpy(backup, bm, fGif->SWidth * fGif->SHeight * sizeof(argb));
}
}
static jboolean reset(GifInfo *info) {
if (fileRewindFun(info) != 0) {
return JNI_FALSE;
}
info->nextStartTime = 0;
info->currentLoop = -1;
info->currentIndex = -1;
return JNI_TRUE;
}
static void getBitmap(argb *bm, GifInfo *info) {
GifFileType *fGIF = info->gifFilePtr;
argb paintingColor;
int i = info->currentIndex;
if (DDGifSlurp(fGIF, info, true) == GIF_ERROR) {
return; //TODO add leniency support
}
SavedImage *cur = &fGIF->SavedImages[i];
short transpIndex = info->infos[i].transpIndex;
if (i == 0) {
if (transpIndex == -1) {
getColorFromTable(fGIF->SBackGroundColor, &paintingColor, fGIF->SColorMap);
} else {
packARGB32(&paintingColor, 0, 0, 0, 0);
}
eraseColor(bm, fGIF->SWidth, fGIF->SHeight, paintingColor);
} else {
disposeFrameIfNeeded(bm, info, i);
}
drawFrame(bm, fGIF->SWidth, fGIF->SHeight, cur, fGIF->SColorMap, transpIndex);
}
static void setMetaData(int width, int height, int ImageCount, int errorCode, JNIEnv *env, jintArray metaData) {
jint *ints = (*env)->GetIntArrayElements(env, metaData, 0);
*ints++ = width;
*ints++ = height;
*ints++ = ImageCount;
*ints = errorCode;
(*env)->ReleaseIntArrayElements(env, metaData, ints, 0);
}
static jint open(GifFileType *GifFileIn, int Error, int startPos, JNIEnv *env, jintArray metaData) {
if (startPos < 0) {
Error = D_GIF_ERR_NOT_READABLE;
DGifCloseFile(GifFileIn);
}
if (Error != 0 || GifFileIn == NULL) {
setMetaData(0, 0, 0, Error, env, metaData);
return (jint) NULL;
}
int width = GifFileIn->SWidth, height = GifFileIn->SHeight;
unsigned int wxh = width * height;
if (wxh < 1 || wxh > INT_MAX) {
DGifCloseFile(GifFileIn);
setMetaData(width, height, 0, D_GIF_ERR_INVALID_SCR_DIMS, env, metaData);
return (jint) NULL;
}
GifInfo *info = malloc(sizeof(GifInfo));
if (info == NULL) {
DGifCloseFile(GifFileIn);
setMetaData(width, height, 0, D_GIF_ERR_NOT_ENOUGH_MEM, env, metaData);
return (jint) NULL;
}
info->gifFilePtr = GifFileIn;
info->startPos = startPos;
info->currentIndex = -1;
info->nextStartTime = 0;
info->lastFrameReaminder = ULONG_MAX;
info->comment = NULL;
info->loopCount = 0;
info->currentLoop = -1;
info->speedFactor = 1.0;
info->rasterBits = calloc(GifFileIn->SHeight * GifFileIn->SWidth, sizeof(GifPixelType));
info->infos = malloc(sizeof(FrameInfo));
info->infos->duration = 0;
info->infos->disposalMethod = 0;
info->infos->transpIndex = -1;
info->backupPtr = NULL;
if (info->rasterBits == NULL || info->infos == NULL) {
cleanUp(info);
setMetaData(width, height, 0, D_GIF_ERR_NOT_ENOUGH_MEM, env, metaData);
return (jint) NULL;
}
if (DDGifSlurp(GifFileIn, info, false) == GIF_ERROR) {
Error = GifFileIn->Error;
}
if (GifFileIn->SColorMap == NULL || GifFileIn->SColorMap->ColorCount != (1 << GifFileIn->SColorMap->BitsPerPixel)) {
GifFreeMapObject(GifFileIn->SColorMap);
GifFileIn->SColorMap = defaultCmap;
}
int imgCount = GifFileIn->ImageCount;
if (imgCount < 1) {
Error = D_GIF_ERR_NO_FRAMES;
}
if (fileRewindFun(info) != 0) {
Error = D_GIF_ERR_READ_FAILED;
}
if (Error != 0) {
cleanUp(info);
}
setMetaData(width, height, imgCount, Error, env, metaData);
return (jint)(Error == 0 ? info : NULL);
}
JNIEXPORT jlong JNICALL Java_org_telegram_ui_Views_GifDrawable_getAllocationByteCount(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return 0;
}
unsigned int pxCount = info->gifFilePtr->SWidth + info->gifFilePtr->SHeight;
jlong sum = pxCount * sizeof(char);
if (info->backupPtr != NULL) {
sum += pxCount * sizeof(argb);
}
return sum;
}
JNIEXPORT jboolean JNICALL Java_org_telegram_ui_Views_GifDrawable_reset(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return JNI_FALSE;
}
return reset(info);
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_setSpeedFactor(JNIEnv *env, jclass class, jobject gifInfo, jfloat factor) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return;
}
info->speedFactor = factor;
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_seekToTime(JNIEnv *env, jclass class, jobject gifInfo, jint desiredPos, jintArray jPixels) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return;
}
int imgCount = info->gifFilePtr->ImageCount;
if (imgCount <= 1) {
return;
}
unsigned long sum = 0;
int i;
for (i = 0; i < imgCount; i++) {
unsigned long newSum = sum + info->infos[i].duration;
if (newSum >= desiredPos) {
break;
}
sum = newSum;
}
if (i < info->currentIndex) {
return;
}
unsigned long lastFrameRemainder = desiredPos - sum;
if (i == imgCount - 1 && lastFrameRemainder > info->infos[i].duration) {
lastFrameRemainder = info->infos[i].duration;
}
info->lastFrameReaminder = lastFrameRemainder;
if (i > info->currentIndex) {
jint *pixels = (*env)->GetIntArrayElements(env, jPixels, 0);
while (info->currentIndex <= i) {
info->currentIndex++;
getBitmap((argb *)pixels, info);
}
(*env)->ReleaseIntArrayElements(env, jPixels, pixels, 0);
}
if (info->speedFactor == 1.0) {
info->nextStartTime = getRealTime() + lastFrameRemainder;
} else {
info->nextStartTime = getRealTime() + lastFrameRemainder * info->speedFactor;
}
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_seekToFrame(JNIEnv *env, jclass class, jobject gifInfo, jint desiredIdx, jintArray jPixels) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return;
}
if (desiredIdx <= info->currentIndex) {
return;
}
int imgCount = info->gifFilePtr->ImageCount;
if (imgCount <= 1) {
return;
}
if (desiredIdx >= imgCount) {
desiredIdx = imgCount - 1;
}
info->lastFrameReaminder = 0;
jint *pixels = (*env)->GetIntArrayElements(env, jPixels, 0);
while (info->currentIndex < desiredIdx) {
info->currentIndex++;
getBitmap((argb *)pixels, info);
}
(*env)->ReleaseIntArrayElements(env, jPixels, pixels, 0);
if (info->speedFactor == 1.0) {
info->nextStartTime = getRealTime() + info->infos[info->currentIndex].duration;
} else {
info->nextStartTime = getRealTime() + info->infos[info->currentIndex].duration * info->speedFactor;
}
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_renderFrame(JNIEnv *env, jclass class, jintArray jPixels, jobject gifInfo, jintArray metaData) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return;
}
bool needRedraw = false;
unsigned long rt = getRealTime();
if (rt >= info->nextStartTime && info->currentLoop < info->loopCount) {
if (++info->currentIndex >= info->gifFilePtr->ImageCount) {
info->currentIndex = 0;
}
needRedraw = true;
}
jint *rawMetaData = (*env)->GetIntArrayElements(env, metaData, 0);
if (needRedraw) {
jint *pixels = (*env)->GetIntArrayElements(env, jPixels, 0);
getBitmap((argb *)pixels, info);
rawMetaData[3] = info->gifFilePtr->Error;
(*env)->ReleaseIntArrayElements(env, jPixels, pixels, 0);
int scaledDuration = info->infos[info->currentIndex].duration;
if (info->speedFactor != 1.0) {
scaledDuration /= info->speedFactor;
}
info->nextStartTime = rt + scaledDuration;
rawMetaData[4] = scaledDuration;
} else {
rawMetaData[4] = (int) (rt - info->nextStartTime);
}
(*env)->ReleaseIntArrayElements(env, metaData, rawMetaData, 0);
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_free(JNIEnv *env, jclass class, jobject gifInfo) {
if (gifInfo == NULL) {
return;
}
GifInfo *info = (GifInfo *)gifInfo;
FILE *file = info->gifFilePtr->UserData;
if (file) {
fclose(file);
}
info->gifFilePtr->UserData = NULL;
cleanUp(info);
}
JNIEXPORT jstring JNICALL Java_org_telegram_ui_Views_GifDrawable_getComment(JNIEnv *env, jclass class, jobject gifInfo) {
if (gifInfo == NULL) {
return NULL;
}
GifInfo *info = (GifInfo *)gifInfo;
return (*env)->NewStringUTF(env, info->comment);
}
JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getLoopCount(JNIEnv *env, jclass class, jobject gifInfo) {
if (gifInfo == NULL) {
return 0;
}
return ((GifInfo *)gifInfo)->loopCount;
}
JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getDuration(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return 0;
}
int i;
unsigned long sum = 0;
for (i = 0; i < info->gifFilePtr->ImageCount; i++) {
sum += info->infos[i].duration;
}
return sum;
}
JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getCurrentPosition(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return 0;
}
int idx = info->currentIndex;
if (idx < 0 || info->gifFilePtr->ImageCount <= 1) {
return 0;
}
int i;
unsigned int sum = 0;
for (i = 0; i < idx; i++) {
sum += info->infos[i].duration;
}
unsigned long remainder = info->lastFrameReaminder == ULONG_MAX ? getRealTime() - info->nextStartTime : info->lastFrameReaminder;
return (int) (sum + remainder);
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_saveRemainder(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return;
}
info->lastFrameReaminder = getRealTime() - info->nextStartTime;
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_restoreRemainder(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL || info->lastFrameReaminder == ULONG_MAX) {
return;
}
info->nextStartTime = getRealTime() + info->lastFrameReaminder;
info->lastFrameReaminder = ULONG_MAX;
}
JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_openFile(JNIEnv *env, jclass class, jintArray metaData, jstring jfname) {
if (jfname == NULL) {
setMetaData(0, 0, 0, D_GIF_ERR_OPEN_FAILED, env, metaData);
return (jint) NULL;
}
const char *fname = (*env)->GetStringUTFChars(env, jfname, 0);
FILE *file = fopen(fname, "rb");
(*env)->ReleaseStringUTFChars(env, jfname, fname);
if (file == NULL) {
setMetaData(0, 0, 0, D_GIF_ERR_OPEN_FAILED, env, metaData);
return (jint) NULL;
}
int Error = 0;
GifFileType *GifFileIn = DGifOpen(file, &fileReadFunc, &Error);
return open(GifFileIn, Error, ftell(file), env, metaData);
}

7
TMessagesProj/jni/gif.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef gif_h
#define gif_h
jint gifOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env);
void gifOnJNIUnload(JavaVM *vm, void *reserved);
#endif

View File

@ -0,0 +1,13 @@
// giflib config.h
#ifndef GIF_CONFIG_H_DEFINED
#define GIF_CONFIG_H_DEFINED
#include <sys/types.h>
#define HAVE_STDINT_H
#define HAVE_FCNTL_H
typedef uint32_t UINT32;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,132 @@
/*****************************************************************************
gif_hash.c -- module to support the following operations:
1. InitHashTable - initialize hash table.
2. ClearHashTable - clear the hash table to an empty state.
2. InsertHashTable - insert one item into data structure.
3. ExistsHashTable - test if item exists in data structure.
This module is used to hash the GIF codes during encoding.
*****************************************************************************/
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include "gif_lib.h"
#include "gif_hash.h"
#include "gif_lib_private.h"
/* #define DEBUG_HIT_RATE Debug number of misses per hash Insert/Exists. */
#ifdef DEBUG_HIT_RATE
static long NumberOfTests = 0,
NumberOfMisses = 0;
#endif /* DEBUG_HIT_RATE */
static int KeyItem(uint32_t Item);
/******************************************************************************
Initialize HashTable - allocate the memory needed and clear it. *
******************************************************************************/
GifHashTableType *_InitHashTable(void)
{
GifHashTableType *HashTable;
if ((HashTable = (GifHashTableType *) malloc(sizeof(GifHashTableType)))
== NULL)
return NULL;
_ClearHashTable(HashTable);
return HashTable;
}
/******************************************************************************
Routine to clear the HashTable to an empty state. *
This part is a little machine depended. Use the commented part otherwise. *
******************************************************************************/
void _ClearHashTable(GifHashTableType *HashTable)
{
memset(HashTable -> HTable, 0xFF, HT_SIZE * sizeof(uint32_t));
}
/******************************************************************************
Routine to insert a new Item into the HashTable. The data is assumed to be *
new one. *
******************************************************************************/
void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code)
{
int HKey = KeyItem(Key);
uint32_t *HTable = HashTable -> HTable;
#ifdef DEBUG_HIT_RATE
NumberOfTests++;
NumberOfMisses++;
#endif /* DEBUG_HIT_RATE */
while (HT_GET_KEY(HTable[HKey]) != 0xFFFFFL) {
#ifdef DEBUG_HIT_RATE
NumberOfMisses++;
#endif /* DEBUG_HIT_RATE */
HKey = (HKey + 1) & HT_KEY_MASK;
}
HTable[HKey] = HT_PUT_KEY(Key) | HT_PUT_CODE(Code);
}
/******************************************************************************
Routine to test if given Key exists in HashTable and if so returns its code *
Returns the Code if key was found, -1 if not. *
******************************************************************************/
int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key)
{
int HKey = KeyItem(Key);
uint32_t *HTable = HashTable -> HTable, HTKey;
#ifdef DEBUG_HIT_RATE
NumberOfTests++;
NumberOfMisses++;
#endif /* DEBUG_HIT_RATE */
while ((HTKey = HT_GET_KEY(HTable[HKey])) != 0xFFFFFL) {
#ifdef DEBUG_HIT_RATE
NumberOfMisses++;
#endif /* DEBUG_HIT_RATE */
if (Key == HTKey) return HT_GET_CODE(HTable[HKey]);
HKey = (HKey + 1) & HT_KEY_MASK;
}
return -1;
}
/******************************************************************************
Routine to generate an HKey for the hashtable out of the given unique key. *
The given Key is assumed to be 20 bits as follows: lower 8 bits are the *
new postfix character, while the upper 12 bits are the prefix code. *
Because the average hit ratio is only 2 (2 hash references per entry), *
evaluating more complex keys (such as twin prime keys) does not worth it! *
******************************************************************************/
static int KeyItem(uint32_t Item)
{
return ((Item >> 12) ^ Item) & HT_KEY_MASK;
}
#ifdef DEBUG_HIT_RATE
/******************************************************************************
Debugging routine to print the hit ratio - number of times the hash table *
was tested per operation. This routine was used to test the KeyItem routine *
******************************************************************************/
void HashTablePrintHitRatio(void)
{
printf("Hash Table Hit Ratio is %ld/%ld = %ld%%.\n",
NumberOfMisses, NumberOfTests,
NumberOfMisses * 100 / NumberOfTests);
}
#endif /* DEBUG_HIT_RATE */
/* end */

View File

@ -0,0 +1,39 @@
/******************************************************************************
gif_hash.h - magfic constants and declarations for GIF LZW
******************************************************************************/
#ifndef _GIF_HASH_H_
#define _GIF_HASH_H_
#include <unistd.h>
#include <stdint.h>
#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */
#define HT_KEY_MASK 0x1FFF /* 13bits keys */
#define HT_KEY_NUM_BITS 13 /* 13bits keys */
#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */
#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
/* The 32 bits of the long are divided into two parts for the key & code: */
/* 1. The code is 12 bits as our compression algorithm is limited to 12bits */
/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */
/* The key is the upper 20 bits. The code is the lower 12. */
#define HT_GET_KEY(l) (l >> 12)
#define HT_GET_CODE(l) (l & 0x0FFF)
#define HT_PUT_KEY(l) (l << 12)
#define HT_PUT_CODE(l) (l & 0x0FFF)
typedef struct GifHashTableType {
uint32_t HTable[HT_SIZE];
} GifHashTableType;
GifHashTableType *_InitHashTable(void);
void _ClearHashTable(GifHashTableType *HashTable);
void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code);
int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key);
#endif /* _GIF_HASH_H_ */
/* end */

View File

@ -0,0 +1,307 @@
/******************************************************************************
gif_lib.h - service library for decoding and encoding GIF images
*****************************************************************************/
#ifndef _GIF_LIB_H_
#define _GIF_LIB_H_ 1
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define GIFLIB_MAJOR 5
#define GIFLIB_MINOR 0
#define GIFLIB_RELEASE 5
#define GIF_ERROR 0
#define GIF_OK 1
#include <stddef.h>
#include <stdbool.h>
#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */
#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1
#define GIF_VERSION_POS 3 /* Version first character in stamp. */
#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */
#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */
typedef unsigned char GifPixelType;
typedef unsigned char *GifRowType;
typedef unsigned char GifByteType;
typedef unsigned int GifPrefixType;
typedef int GifWord;
typedef struct GifColorType {
GifByteType Red, Green, Blue;
} GifColorType;
typedef struct ColorMapObject {
int ColorCount;
int BitsPerPixel;
bool SortFlag;
GifColorType *Colors; /* on malloc(3) heap */
} ColorMapObject;
typedef struct GifImageDesc {
GifWord Left, Top, Width, Height; /* Current image dimensions. */
bool Interlace; /* Sequential/Interlaced lines. */
ColorMapObject *ColorMap; /* The local color map */
} GifImageDesc;
typedef struct ExtensionBlock {
int ByteCount;
GifByteType *Bytes; /* on malloc(3) heap */
int Function; /* The block function code */
#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */
#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */
#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */
#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */
#define APPLICATION_EXT_FUNC_CODE 0xff /* application block */
} ExtensionBlock;
typedef struct SavedImage {
GifImageDesc ImageDesc;
GifByteType *RasterBits; /* on malloc(3) heap */
int ExtensionBlockCount; /* Count of extensions before image */
ExtensionBlock *ExtensionBlocks; /* Extensions before image */
} SavedImage;
typedef struct GifFileType {
GifWord SWidth, SHeight; /* Size of virtual canvas */
GifWord SColorResolution; /* How many colors can we generate? */
GifWord SBackGroundColor; /* Background color for virtual canvas */
GifByteType AspectByte; /* Used to compute pixel aspect ratio */
ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */
int ImageCount; /* Number of current image (both APIs) */
GifImageDesc Image; /* Current image (low-level API) */
SavedImage *SavedImages; /* Image sequence (high-level API) */
int ExtensionBlockCount; /* Count extensions past last image */
ExtensionBlock *ExtensionBlocks; /* Extensions past last image */
int Error; /* Last error condition reported */
void *UserData; /* hook to attach user data (TVT) */
void *Private; /* Don't mess with this! */
} GifFileType;
#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0)
typedef enum {
UNDEFINED_RECORD_TYPE,
SCREEN_DESC_RECORD_TYPE,
IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */
EXTENSION_RECORD_TYPE, /* Begin with '!' */
TERMINATE_RECORD_TYPE /* Begin with ';' */
} GifRecordType;
/* func type to read gif data from arbitrary sources (TVT) */
typedef int (*InputFunc) (GifFileType *, GifByteType *, int);
/* func type to write gif data to arbitrary targets.
* Returns count of bytes written. (MRB)
*/
typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int);
/******************************************************************************
GIF89 structures
******************************************************************************/
typedef struct GraphicsControlBlock {
int DisposalMode;
#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */
#define DISPOSE_DO_NOT 1 /* Leave image in place */
#define DISPOSE_BACKGROUND 2 /* Set area too background color */
#define DISPOSE_PREVIOUS 3 /* Restore to previous content */
bool UserInputFlag; /* User confirmation required before disposal */
int DelayTime; /* pre-display delay in 0.01sec units */
int TransparentColor; /* Palette index for transparency, -1 if none */
#define NO_TRANSPARENT_COLOR -1
} GraphicsControlBlock;
/******************************************************************************
GIF encoding routines
******************************************************************************/
/* Main entry points */
GifFileType *EGifOpenFileName(const char *GifFileName,
const bool GifTestExistence, int *Error);
GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error);
GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error);
int EGifSpew(GifFileType * GifFile);
char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */
int EGifCloseFile(GifFileType * GifFile);
#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */
#define E_GIF_ERR_WRITE_FAILED 2
#define E_GIF_ERR_HAS_SCRN_DSCR 3
#define E_GIF_ERR_HAS_IMAG_DSCR 4
#define E_GIF_ERR_NO_COLOR_MAP 5
#define E_GIF_ERR_DATA_TOO_BIG 6
#define E_GIF_ERR_NOT_ENOUGH_MEM 7
#define E_GIF_ERR_DISK_IS_FULL 8
#define E_GIF_ERR_CLOSE_FAILED 9
#define E_GIF_ERR_NOT_WRITEABLE 10
/* These are legacy. You probably do not want to call them directly */
int EGifPutScreenDesc(GifFileType *GifFile,
const int GifWidth, const int GifHeight,
const int GifColorRes,
const int GifBackGround,
const ColorMapObject *GifColorMap);
int EGifPutImageDesc(GifFileType *GifFile,
const int GifLeft, const int GifTop,
const int GifWidth, const int GifHeight,
const bool GifInterlace,
const ColorMapObject *GifColorMap);
void EGifSetGifVersion(GifFileType *GifFile, const bool gif89);
int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine,
int GifLineLen);
int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel);
int EGifPutComment(GifFileType *GifFile, const char *GifComment);
int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode);
int EGifPutExtensionBlock(GifFileType *GifFile,
const int GifExtLen, const void *GifExtension);
int EGifPutExtensionTrailer(GifFileType *GifFile);
int EGifPutExtension(GifFileType *GifFile, const int GifExtCode,
const int GifExtLen,
const void *GifExtension);
int EGifPutCode(GifFileType *GifFile, int GifCodeSize,
const GifByteType *GifCodeBlock);
int EGifPutCodeNext(GifFileType *GifFile,
const GifByteType *GifCodeBlock);
/******************************************************************************
GIF decoding routines
******************************************************************************/
/* Main entry points */
GifFileType *DGifOpenFileName(const char *GifFileName, int *Error);
GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error);
int DGifSlurp(GifFileType * GifFile);
GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */
int DGifCloseFile(GifFileType * GifFile);
#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */
#define D_GIF_ERR_READ_FAILED 102
#define D_GIF_ERR_NOT_GIF_FILE 103
#define D_GIF_ERR_NO_SCRN_DSCR 104
#define D_GIF_ERR_NO_IMAG_DSCR 105
#define D_GIF_ERR_NO_COLOR_MAP 106
#define D_GIF_ERR_WRONG_RECORD 107
#define D_GIF_ERR_DATA_TOO_BIG 108
#define D_GIF_ERR_NOT_ENOUGH_MEM 109
#define D_GIF_ERR_CLOSE_FAILED 110
#define D_GIF_ERR_NOT_READABLE 111
#define D_GIF_ERR_IMAGE_DEFECT 112
#define D_GIF_ERR_EOF_TOO_SOON 113
/* These are legacy. You probably do not want to call them directly */
int DGifGetScreenDesc(GifFileType *GifFile);
int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType);
int DGifGetImageDesc(GifFileType *GifFile, bool changeImageCount);
int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen);
int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel);
int DGifGetComment(GifFileType *GifFile, char *GifComment);
int DGifGetExtension(GifFileType *GifFile, int *GifExtCode,
GifByteType **GifExtension);
int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension,int* ExtCode);
int DGifGetCode(GifFileType *GifFile, int *GifCodeSize,
GifByteType **GifCodeBlock);
int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock);
int DGifGetLZCodes(GifFileType *GifFile, int *GifCode);
/******************************************************************************
Color table quantization (deprecated)
******************************************************************************/
int GifQuantizeBuffer(unsigned int Width, unsigned int Height,
int *ColorMapSize, GifByteType * RedInput,
GifByteType * GreenInput, GifByteType * BlueInput,
GifByteType * OutputBuffer,
GifColorType * OutputColorMap);
/******************************************************************************
Error handling and reporting.
******************************************************************************/
extern char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */
/*****************************************************************************
Everything below this point is new after version 1.2, supporting `slurp
mode' for doing I/O in two big belts with all the image-bashing in core.
******************************************************************************/
/******************************************************************************
Color map handling from gif_alloc.c
******************************************************************************/
extern ColorMapObject *GifMakeMapObject(int ColorCount,
const GifColorType *ColorMap);
extern void GifFreeMapObject(ColorMapObject *Object);
extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1,
const ColorMapObject *ColorIn2,
GifPixelType ColorTransIn2[]);
extern int GifBitSize(int n);
/******************************************************************************
Support for the in-core structures allocation (slurp mode).
******************************************************************************/
extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]);
extern int GifAddExtensionBlock(int *ExtensionBlock_Count,
ExtensionBlock **ExtensionBlocks,
int Function,
unsigned int Len, unsigned char ExtData[]);
extern void GifFreeExtensions(int *ExtensionBlock_Count,
ExtensionBlock **ExtensionBlocks);
extern SavedImage *GifMakeSavedImage(GifFileType *GifFile,
const SavedImage *CopyFrom);
extern void GifFreeSavedImages(GifFileType *GifFile);
/******************************************************************************
5.x functions for GIF89 graphics control blocks
******************************************************************************/
int DGifExtensionToGCB(const size_t GifExtensionLength,
const GifByteType *GifExtension,
GraphicsControlBlock *GCB);
size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
GifByteType *GifExtension);
int DGifSavedExtensionToGCB(GifFileType *GifFile,
int ImageIndex,
GraphicsControlBlock *GCB);
int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
GifFileType *GifFile,
int ImageIndex);
/******************************************************************************
The library's internal utility font
******************************************************************************/
#define GIF_FONT_WIDTH 8
#define GIF_FONT_HEIGHT 8
extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH];
extern void GifDrawText8x8(SavedImage *Image,
const int x, const int y,
const char *legend, const int color);
extern void GifDrawBox(SavedImage *Image,
const int x, const int y,
const int w, const int d, const int color);
extern void GifDrawRectangle(SavedImage *Image,
const int x, const int y,
const int w, const int d, const int color);
extern void GifDrawBoxedText8x8(SavedImage *Image,
const int x, const int y,
const char *legend,
const int border, const int bg, const int fg);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _GIF_LIB_H */
/* end */

View File

@ -0,0 +1,59 @@
/****************************************************************************
gif_lib_private.h - internal giflib routines and structures
****************************************************************************/
#ifndef _GIF_LIB_PRIVATE_H
#define _GIF_LIB_PRIVATE_H
#include "gif_lib.h"
#include "gif_hash.h"
#define EXTENSION_INTRODUCER 0x21
#define DESCRIPTOR_INTRODUCER 0x2c
#define TERMINATOR_INTRODUCER 0x3b
#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
#define LZ_BITS 12
#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */
#define FIRST_CODE 4097 /* Impossible code, to signal first. */
#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */
#define FILE_STATE_WRITE 0x01
#define FILE_STATE_SCREEN 0x02
#define FILE_STATE_IMAGE 0x04
#define FILE_STATE_READ 0x08
#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ)
#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE)
typedef struct GifFilePrivateType {
GifWord FileState, FileHandle, /* Where all this data goes to! */
BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */
ClearCode, /* The CLEAR LZ code. */
EOFCode, /* The EOF LZ code. */
RunningCode, /* The next code algorithm can generate. */
RunningBits, /* The number of bits required to represent RunningCode. */
MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */
LastCode, /* The code before the current code. */
CrntCode, /* Current algorithm code. */
StackPtr, /* For character stack (see below). */
CrntShiftState; /* Number of bits in CrntShiftDWord. */
unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */
unsigned long PixelCount; /* Number of pixels in image. */
FILE *File; /* File as stream. */
InputFunc Read; /* function to read gif input (TVT) */
OutputFunc Write; /* function to write gif output (MRB) */
GifByteType Buf[256]; /* Compressed input is buffered here. */
GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */
GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */
GifPrefixType Prefix[LZ_MAX_CODE + 1];
GifHashTableType *HashTable;
bool gif89;
} GifFilePrivateType;
#endif /* _GIF_LIB_PRIVATE_H */
/* end */

View File

@ -0,0 +1,400 @@
/*****************************************************************************
GIF construction tools
****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "gif_lib.h"
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
/******************************************************************************
Miscellaneous utility functions
******************************************************************************/
/* return smallest bitfield size n will fit in */
int
GifBitSize(int n)
{
register int i;
for (i = 1; i <= 8; i++)
if ((1 << i) >= n)
break;
return (i);
}
/******************************************************************************
Color map object functions
******************************************************************************/
/*
* Allocate a color map of given size; initialize with contents of
* ColorMap if that pointer is non-NULL.
*/
ColorMapObject *
GifMakeMapObject(int ColorCount, const GifColorType *ColorMap)
{
ColorMapObject *Object;
/*** FIXME: Our ColorCount has to be a power of two. Is it necessary to
* make the user know that or should we automatically round up instead? */
if (ColorCount != (1 << GifBitSize(ColorCount))) {
return ((ColorMapObject *) NULL);
}
Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
if (Object == (ColorMapObject *) NULL) {
return ((ColorMapObject *) NULL);
}
Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
if (Object->Colors == (GifColorType *) NULL) {
free(Object);
return ((ColorMapObject *) NULL);
}
Object->ColorCount = ColorCount;
Object->BitsPerPixel = GifBitSize(ColorCount);
if (ColorMap != NULL) {
memcpy((char *)Object->Colors,
(char *)ColorMap, ColorCount * sizeof(GifColorType));
}
return (Object);
}
/*******************************************************************************
Free a color map object
*******************************************************************************/
void
GifFreeMapObject(ColorMapObject *Object)
{
if (Object != NULL) {
(void)free(Object->Colors);
(void)free(Object);
}
}
#ifdef DEBUG
void
DumpColorMap(ColorMapObject *Object,
FILE * fp)
{
if (Object != NULL) {
int i, j, Len = Object->ColorCount;
for (i = 0; i < Len; i += 4) {
for (j = 0; j < 4 && j < Len; j++) {
(void)fprintf(fp, "%3d: %02x %02x %02x ", i + j,
Object->Colors[i + j].Red,
Object->Colors[i + j].Green,
Object->Colors[i + j].Blue);
}
(void)fprintf(fp, "\n");
}
}
}
#endif /* DEBUG */
/*******************************************************************************
Compute the union of two given color maps and return it. If result can't
fit into 256 colors, NULL is returned, the allocated union otherwise.
ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
copied iff they didn't exist before. ColorTransIn2 maps the old
ColorIn2 into the ColorUnion color map table./
*******************************************************************************/
ColorMapObject *
GifUnionColorMap(const ColorMapObject *ColorIn1,
const ColorMapObject *ColorIn2,
GifPixelType ColorTransIn2[])
{
int i, j, CrntSlot, RoundUpTo, NewGifBitSize;
ColorMapObject *ColorUnion;
/*
* We don't worry about duplicates within either color map; if
* the caller wants to resolve those, he can perform unions
* with an empty color map.
*/
/* Allocate table which will hold the result for sure. */
ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount,
ColorIn2->ColorCount) * 2, NULL);
if (ColorUnion == NULL)
return (NULL);
/*
* Copy ColorIn1 to ColorUnion.
*/
for (i = 0; i < ColorIn1->ColorCount; i++)
ColorUnion->Colors[i] = ColorIn1->Colors[i];
CrntSlot = ColorIn1->ColorCount;
/*
* Potentially obnoxious hack:
*
* Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
* of table 1. This is very useful if your display is limited to
* 16 colors.
*/
while (ColorIn1->Colors[CrntSlot - 1].Red == 0
&& ColorIn1->Colors[CrntSlot - 1].Green == 0
&& ColorIn1->Colors[CrntSlot - 1].Blue == 0)
CrntSlot--;
/* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
/* Let's see if this color already exists: */
for (j = 0; j < ColorIn1->ColorCount; j++)
if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i],
sizeof(GifColorType)) == 0)
break;
if (j < ColorIn1->ColorCount)
ColorTransIn2[i] = j; /* color exists in Color1 */
else {
/* Color is new - copy it to a new slot: */
ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
ColorTransIn2[i] = CrntSlot++;
}
}
if (CrntSlot > 256) {
GifFreeMapObject(ColorUnion);
return ((ColorMapObject *) NULL);
}
NewGifBitSize = GifBitSize(CrntSlot);
RoundUpTo = (1 << NewGifBitSize);
if (RoundUpTo != ColorUnion->ColorCount) {
register GifColorType *Map = ColorUnion->Colors;
/*
* Zero out slots up to next power of 2.
* We know these slots exist because of the way ColorUnion's
* start dimension was computed.
*/
for (j = CrntSlot; j < RoundUpTo; j++)
Map[j].Red = Map[j].Green = Map[j].Blue = 0;
/* perhaps we can shrink the map? */
if (RoundUpTo < ColorUnion->ColorCount)
ColorUnion->Colors = (GifColorType *)realloc(Map,
sizeof(GifColorType) * RoundUpTo);
}
ColorUnion->ColorCount = RoundUpTo;
ColorUnion->BitsPerPixel = NewGifBitSize;
return (ColorUnion);
}
/*******************************************************************************
Apply a given color translation to the raster bits of an image
*******************************************************************************/
void
GifApplyTranslation(SavedImage *Image, GifPixelType Translation[])
{
register int i;
register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
for (i = 0; i < RasterSize; i++)
Image->RasterBits[i] = Translation[Image->RasterBits[i]];
}
/******************************************************************************
Extension record functions
******************************************************************************/
int
GifAddExtensionBlock(int *ExtensionBlockCount,
ExtensionBlock **ExtensionBlocks,
int Function,
unsigned int Len,
unsigned char ExtData[])
{
ExtensionBlock *ep;
if (*ExtensionBlocks == NULL)
*ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
else
*ExtensionBlocks = (ExtensionBlock *)realloc(*ExtensionBlocks,
sizeof(ExtensionBlock) *
(*ExtensionBlockCount + 1));
if (*ExtensionBlocks == NULL)
return (GIF_ERROR);
ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
ep->Function = Function;
ep->ByteCount=Len;
ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
if (ep->Bytes == NULL)
return (GIF_ERROR);
if (ExtData != NULL) {
memcpy(ep->Bytes, ExtData, Len);
}
return (GIF_OK);
}
void
GifFreeExtensions(int *ExtensionBlockCount,
ExtensionBlock **ExtensionBlocks)
{
ExtensionBlock *ep;
if (*ExtensionBlocks == NULL)
return;
for (ep = *ExtensionBlocks;
ep < (*ExtensionBlocks + *ExtensionBlockCount);
ep++)
(void)free((char *)ep->Bytes);
(void)free((char *)*ExtensionBlocks);
*ExtensionBlocks = NULL;
*ExtensionBlockCount = 0;
}
/******************************************************************************
Image block allocation functions
******************************************************************************/
/* Private Function:
* Frees the last image in the GifFile->SavedImages array
*/
void
FreeLastSavedImage(GifFileType *GifFile)
{
SavedImage *sp;
if ((GifFile == NULL) || (GifFile->SavedImages == NULL))
return;
/* Remove one SavedImage from the GifFile */
GifFile->ImageCount--;
sp = &GifFile->SavedImages[GifFile->ImageCount];
/* Deallocate its Colormap */
if (sp->ImageDesc.ColorMap != NULL) {
GifFreeMapObject(sp->ImageDesc.ColorMap);
sp->ImageDesc.ColorMap = NULL;
}
/* Deallocate the image data */
if (sp->RasterBits != NULL)
free((char *)sp->RasterBits);
/* Deallocate any extensions */
GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
/*** FIXME: We could realloc the GifFile->SavedImages structure but is
* there a point to it? Saves some memory but we'd have to do it every
* time. If this is used in GifFreeSavedImages then it would be inefficient
* (The whole array is going to be deallocated.) If we just use it when
* we want to free the last Image it's convenient to do it here.
*/
}
/*
* Append an image block to the SavedImages array
*/
SavedImage *
GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
{
if (GifFile->SavedImages == NULL)
GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
else
GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
sizeof(SavedImage) * (GifFile->ImageCount + 1));
if (GifFile->SavedImages == NULL)
return ((SavedImage *)NULL);
else {
SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++];
memset((char *)sp, '\0', sizeof(SavedImage));
if (CopyFrom != NULL) {
memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
/*
* Make our own allocated copies of the heap fields in the
* copied record. This guards against potential aliasing
* problems.
*/
/* first, the local color map */
if (sp->ImageDesc.ColorMap != NULL) {
sp->ImageDesc.ColorMap = GifMakeMapObject(
CopyFrom->ImageDesc.ColorMap->ColorCount,
CopyFrom->ImageDesc.ColorMap->Colors);
if (sp->ImageDesc.ColorMap == NULL) {
FreeLastSavedImage(GifFile);
return (SavedImage *)(NULL);
}
}
/* next, the raster */
sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) *
CopyFrom->ImageDesc.Height *
CopyFrom->ImageDesc.Width);
if (sp->RasterBits == NULL) {
FreeLastSavedImage(GifFile);
return (SavedImage *)(NULL);
}
memcpy(sp->RasterBits, CopyFrom->RasterBits,
sizeof(GifPixelType) * CopyFrom->ImageDesc.Height *
CopyFrom->ImageDesc.Width);
/* finally, the extension blocks */
if (sp->ExtensionBlocks != NULL) {
sp->ExtensionBlocks = (ExtensionBlock *)malloc(
sizeof(ExtensionBlock) *
CopyFrom->ExtensionBlockCount);
if (sp->ExtensionBlocks == NULL) {
FreeLastSavedImage(GifFile);
return (SavedImage *)(NULL);
}
memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
}
}
return (sp);
}
}
void
GifFreeSavedImages(GifFileType *GifFile)
{
SavedImage *sp;
if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
return;
}
for (sp = GifFile->SavedImages;
sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
if (sp->ImageDesc.ColorMap != NULL) {
GifFreeMapObject(sp->ImageDesc.ColorMap);
sp->ImageDesc.ColorMap = NULL;
}
if (sp->RasterBits != NULL)
free((char *)sp->RasterBits);
GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
}
free((char *)GifFile->SavedImages);
GifFile->SavedImages = NULL;
}
/* end */

View File

@ -3,9 +3,34 @@
#include <jni.h>
#include <sys/types.h>
#include <inttypes.h>
#include <android/log.h>
#include "aes.h"
#include "log.h"
#include <stdlib.h>
#include "aes/aes.h"
#include "utils.h"
#include "sqlite.h"
#include "gif.h"
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = 0;
srand(time(NULL));
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (sqliteOnJNILoad(vm, reserved, env) == -1) {
return -1;
}
if (gifOnJNILoad(vm, reserved, env) == -1) {
return -1;
}
return JNI_VERSION_1_6;
}
void JNI_OnUnload(JavaVM *vm, void *reserved) {
gifOnJNIUnload(vm, reserved);
}
JNIEXPORT jbyteArray Java_org_telegram_messenger_Utilities_aesIgeEncryption(JNIEnv *env, jclass class, jbyteArray _what, jbyteArray _key, jbyteArray _iv, jboolean encrypt, jboolean changeIv, jint l) {
unsigned char *what = (unsigned char *)(*env)->GetByteArrayElements(env, _what, NULL);

View File

@ -1,9 +0,0 @@
#ifndef __SQLITE_H__
#define __SQLITE_H__
#include <jni.h>
#include "sqlite3.h"
void throw_sqlite3_exception(JNIEnv* env, sqlite3 *handle, int errcode);
#endif

View File

@ -1,5 +1,5 @@
#include "sqlite3.h"
#include "org_telegram_SQLite.h"
#include "sqlite/sqlite3.h"
#include "sqlite.h"
void throw_sqlite3_exception(JNIEnv *env, sqlite3 *handle, int errcode) {
if (SQLITE_OK == errcode) {

10
TMessagesProj/jni/sqlite.h Executable file
View File

@ -0,0 +1,10 @@
#ifndef sqlite_h
#define sqlite_h
#include <jni.h>
#include "sqlite/sqlite3.h"
void throw_sqlite3_exception(JNIEnv* env, sqlite3 *handle, int errcode);
jint sqliteOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env);
#endif

View File

@ -1,4 +1,4 @@
#include "org_telegram_SQLite.h"
#include "sqlite.h"
int Java_org_telegram_SQLite_SQLiteCursor_columnType(JNIEnv *env, jobject object, int statementHandle, int columnIndex) {
sqlite3_stmt *handle = (sqlite3_stmt *)statementHandle;

View File

@ -1,4 +1,4 @@
#include "org_telegram_SQLite.h"
#include "sqlite.h"
void Java_org_telegram_SQLite_SQLiteDatabase_closedb(JNIEnv *env, jobject object, int sqliteHandle) {
sqlite3 *handle = (sqlite3 *)sqliteHandle;

View File

@ -1,21 +1,11 @@
#include <time.h>
#include <stdlib.h>
#include "org_telegram_SQLite.h"
#include "sqlite.h"
jfieldID queryArgsCountField;
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv* env = 0;
srand(time(NULL));
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
jint sqliteOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env) {
jclass class = (*env)->FindClass(env, "org/telegram/SQLite/SQLitePreparedStatement");
queryArgsCountField = (*env)->GetFieldID(env, class, "queryArgsCount", "I");
return JNI_VERSION_1_4;
return JNI_VERSION_1_6;
}
int Java_org_telegram_SQLite_SQLitePreparedStatement_step(JNIEnv* env, jobject object, int statementHandle) {

View File

@ -9,4 +9,11 @@
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#ifndef max
#define max(x, y) ((x) > (y)) ? (x) : (y)
#endif
#ifndef min
#define min(x, y) ((x) < (y)) ? (x) : (y)
#endif
#endif

View File

@ -1,18 +0,0 @@
#include <jni.h>
#include "video.h"
JNIEXPORT void Java_org_telegram_messenger_VideoTools_initialize(JNIEnv* env, jclass class) {
av_register_all();
}
JNIEXPORT void Java_org_telegram_messenger_VideoTools_convert(JNIEnv* env, jclass class, jstring in, jstring out, int bitr) {
char const *in_str = (*env)->GetStringUTFChars(env, in, 0);
char const *out_str = (*env)->GetStringUTFChars(env, out, 0);
convertFile(in_str, out_str, bitr);
if (in_str != 0) {
(*env)->ReleaseStringUTFChars(env, in, in_str);
}
if (out_str != 0) {
(*env)->ReleaseStringUTFChars(env, out, out_str);
}
}

View File

@ -1,17 +0,0 @@
#ifndef video_h
#define video_h
#include <libavformat/avformat.h>
int prepare_for_video_conversion(const char *dst_filename, AVCodecContext *video_dec_ctx, AVCodecContext *audio_dec_ctx, AVFormatContext *fmt_ctx, AVStream *src_video_stream, AVStream *src_audio_stream, int bitr);
int write_video_frame(AVFrame *src_frame);
int write_audio_frame(AVFrame *src_frame, AVCodecContext *src_codec);
void post_video_conversion();
void cleanup_out();
void onError();
void onProgress();
void onDone();
void convertFile(const char *src_filename, const char *dst_filename, int bitr);
#endif

View File

@ -1,243 +0,0 @@
#include "video.h"
#include <stdio.h>
#include <libavutil/timestamp.h>
#include <libavutil/imgutils.h>
#include "log.h"
AVPacket pkt;
int video_stream_idx = -1, audio_stream_idx = -1;
AVCodecContext *video_dec_ctx = NULL, *audio_dec_ctx = NULL;
AVFrame *frame = NULL;
AVStream *video_stream = NULL, *audio_stream = NULL;
AVFormatContext *fmt_ctx = NULL;
int64_t total_duration;
int64_t current_duration;
char *src = NULL;
char *dest = NULL;
int lastLog = 10;
void cleanup_in() {
if (video_dec_ctx) {
avcodec_close(video_dec_ctx);
video_dec_ctx = NULL;
}
if (audio_dec_ctx) {
avcodec_close(audio_dec_ctx);
audio_dec_ctx = NULL;
}
if (fmt_ctx) {
avformat_close_input(&fmt_ctx);
fmt_ctx = NULL;
}
if (frame) {
av_frame_free(&frame);
frame = NULL;
}
if (src) {
free(src);
src = NULL;
}
if (dest) {
free(dest);
dest = NULL;
}
total_duration = 0;
current_duration = 0;
video_stream_idx = -1;
audio_stream_idx = -1;
video_stream = NULL;
audio_stream = NULL;
lastLog = 10;
}
void onError() {
cleanup_in();
cleanup_out();
}
void onDone() {
LOGD("OK\n");
cleanup_in();
cleanup_out();
}
void onProgress() {
float progress = (float)current_duration / (float)total_duration * 100;
if (progress > lastLog) {
lastLog += 10;
LOGD("progress %.2f\n", progress);
}
}
int open_codec_context(int *stream_idx, AVFormatContext *fmt_ctx, enum AVMediaType type) {
int ret;
AVStream *st;
AVCodecContext *dec_ctx = NULL;
AVCodec *dec = NULL;
AVDictionary *opts = NULL;
ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
if (ret < 0) {
LOGD("Could not find %s stream in input file\n", av_get_media_type_string(type));
return ret;
} else {
*stream_idx = ret;
st = fmt_ctx->streams[*stream_idx];
dec_ctx = st->codec;
dec = avcodec_find_decoder(dec_ctx->codec_id);
if (!dec) {
LOGD("Failed to find %s codec\n", av_get_media_type_string(type));
return ret;
}
av_dict_set(&opts, "refcounted_frames", "1", 0);
if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) {
LOGD("Failed to open %s codec\n", av_get_media_type_string(type));
return ret;
}
}
return 0;
}
int decode_packet(int *got_frame, int cached) {
int ret = 0;
int decoded = pkt.size;
*got_frame = 0;
if (pkt.stream_index == video_stream_idx) {
ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);
if (ret < 0) {
LOGD("Error decoding video frame\n");
return ret;
}
if (*got_frame) {
ret = write_video_frame(frame);
if (ret < 0) {
return ret;
}
}
} else if (pkt.stream_index == audio_stream_idx) {
ret = avcodec_decode_audio4(audio_dec_ctx, frame, got_frame, &pkt);
if (ret < 0) {
LOGD("Error decoding audio frame\n");
return ret;
}
decoded = FFMIN(ret, pkt.size);
if (*got_frame) {
ret = write_audio_frame(frame, audio_dec_ctx);
if (ret < 0) {
return -1;
}
}
frame->pts = AV_NOPTS_VALUE;
}
if (*got_frame) {
av_frame_unref(frame);
}
return decoded;
}
void convertFile(const char *src_filename, const char *dst_filename, int bitr) {
int ret = 0;
int got_frame;
src = malloc(strlen(src_filename) + 1);
memcpy(src, src_filename, strlen(src_filename));
src[strlen(src_filename)] = '\0';
dest = malloc(strlen(dst_filename) + 1);
memcpy(dest, dst_filename, strlen(dst_filename));
dest[strlen(dst_filename)] = '\0';
if ((ret = avformat_open_input(&fmt_ctx, src, NULL, NULL)) < 0) {
LOGD("Could not open source file %s, %s\n", src, av_err2str(ret));
onError();
return;
}
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
LOGD("Could not find stream information\n");
onError();
return;
}
if (open_codec_context(&video_stream_idx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
video_stream = fmt_ctx->streams[video_stream_idx];
video_dec_ctx = video_stream->codec;
}
if (open_codec_context(&audio_stream_idx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0) {
audio_stream = fmt_ctx->streams[audio_stream_idx];
audio_dec_ctx = audio_stream->codec;
}
av_dump_format(fmt_ctx, 0, src, 0);
if (!audio_stream && !video_stream) {
LOGD("Could not find audio or video stream in the input, aborting\n");
onError();
return;
}
frame = av_frame_alloc();
if (!frame) {
LOGD("Could not allocate frame\n");
onError();
return;
}
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
if (video_stream) {
LOGD("Demuxing video from file '%s'\n", src);
}
if (audio_stream) {
LOGD("Demuxing audio from file '%s'\n", src);
}
ret = prepare_for_video_conversion(dest, video_dec_ctx, audio_dec_ctx, fmt_ctx, video_stream, audio_stream, bitr);
if (ret < 0) {
return;
}
if (video_stream) {
total_duration = video_stream->duration;
}
if (audio_stream) {
total_duration += audio_stream->duration;
}
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
AVPacket orig_pkt = pkt;
do {
ret = decode_packet(&got_frame, 0);
if (ret < 0) {
onError();
return;
}
pkt.data += ret;
pkt.size -= ret;
current_duration += pkt.duration;
onProgress();
} while (pkt.size > 0);
av_free_packet(&orig_pkt);
}
pkt.data = NULL;
pkt.size = 0;
do {
decode_packet(&got_frame, 1);
} while (got_frame);
post_video_conversion();
onDone();
}

View File

@ -1,366 +0,0 @@
#include "video.h"
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include "log.h"
AVFrame *out_frame = NULL;
struct SwsContext *sws_ctx = NULL;
AVStream *video_st = NULL, *audio_st = NULL;
AVFormatContext *oc = NULL;
AVOutputFormat *fmt = NULL;
AVPicture dst_picture;
uint8_t **dst_samples_data = NULL;
SwrContext *swr_ctx = NULL;
int current_n_out = 0;
int current_in_buff = 0;
uint8_t buff[4096 * 2];
int min(int val1, int val2) {
return val1 < val2 ? val1 : val2;
}
int prepare_for_video_conversion(const char *dst_filename, AVCodecContext *video_dec_ctx, AVCodecContext *audio_dec_ctx, AVFormatContext *fmt_ctx, AVStream *src_video_stream, AVStream *src_audio_stream, int bitr) {
if (!video_dec_ctx && !audio_dec_ctx) {
onError();
return -1;
}
avformat_alloc_output_context2(&oc, NULL, "mp4", dst_filename);
if (!oc) {
onError();
return -1;
}
fmt = oc->oformat;
av_dict_copy(&oc->metadata, fmt_ctx->metadata, 0);
int ret = 0;
if (!(fmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&oc->pb, dst_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
LOGD("Could not open '%s': %s\n", dst_filename, av_err2str(ret));
onError();
return -1;
}
}
AVCodecContext *c;
if (video_dec_ctx && src_video_stream && fmt_ctx) {
//calculate video resolution
int dst_width = video_dec_ctx->width, dst_height = video_dec_ctx->height;
if (video_dec_ctx->width > video_dec_ctx->height) {
if (video_dec_ctx->width > 480) {
float scale = video_dec_ctx->width / 480.0f;
dst_width = 480;
dst_height = ceilf(video_dec_ctx->height / scale);
}
} else {
if (video_dec_ctx->width > 480) {
float scale = video_dec_ctx->height / 480.0f;
dst_height = 480;
dst_width = ceilf(video_dec_ctx->width / scale);
}
}
if (video_dec_ctx->height != dst_height || video_dec_ctx->width != dst_width || video_dec_ctx->pix_fmt != AV_PIX_FMT_YUV420P) {
sws_ctx = sws_getContext(video_dec_ctx->width, video_dec_ctx->height, video_dec_ctx->pix_fmt, dst_width, dst_height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL);
if (!sws_ctx) {
LOGD("Could not initialize the conversion context\n");
onError();
return -1;
}
}
//create video stream
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);
if (!codec) {
LOGD("Could not find encoder for '%s'\n", avcodec_get_name(AV_CODEC_ID_MPEG4));
onError();
return -1;
}
video_st = avformat_new_stream(oc, codec);
if (!video_st) {
LOGD("Could not allocate stream\n");
onError();
return -1;
}
video_st->id = oc->nb_streams - 1;
av_dict_copy(&video_st->metadata, src_video_stream->metadata, 0);
c = video_st->codec;
c->codec_id = AV_CODEC_ID_MPEG4;
c->bit_rate = bitr;
c->width = dst_width;
c->height = dst_height;
double fps = (double)src_video_stream->avg_frame_rate.num / (double)src_video_stream->avg_frame_rate.den;
c->time_base.den = 65535;
c->time_base.num = floor(65635 / fps);
c->gop_size = 12;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if (oc->oformat->flags & AVFMT_GLOBALHEADER) {
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
LOGD("Could not open video codec: %s\n", av_err2str(ret));
onError();
return -1;
}
out_frame = avcodec_alloc_frame();
if (!out_frame) {
LOGD("Could not allocate video frame\n");
onError();
return -1;
}
ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height);
if (ret < 0) {
LOGD("Could not allocate picture: %s\n", av_err2str(ret));
onError();
return -1;
}
*((AVPicture *)out_frame) = dst_picture;
}
//create audio stream
if (audio_dec_ctx && src_audio_stream) {
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if (!codec) {
LOGD("Could not find encoder for '%s'\n", avcodec_get_name(AV_CODEC_ID_AAC));
onError();
return -1;
}
audio_st = avformat_new_stream(oc, codec);
if (!audio_st) {
LOGD("Could not allocate stream\n");
onError();
return -1;
}
audio_st->id = oc->nb_streams - 1;
av_dict_copy(&audio_st->metadata, src_audio_stream->metadata, 0);
c = audio_st->codec;
c->sample_fmt = AV_SAMPLE_FMT_FLTP;
c->bit_rate = 40000;
c->sample_rate = min(audio_dec_ctx->sample_rate, 44100);
c->channels = 1;
if (oc->oformat->flags & AVFMT_GLOBALHEADER) {
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
c = audio_st->codec;
c->strict_std_compliance = -2;
swr_ctx = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_MONO, c->sample_fmt, c->sample_rate, audio_dec_ctx->channel_layout, audio_dec_ctx->sample_fmt, audio_dec_ctx->sample_rate, 0, NULL);
if (!swr_ctx) {
LOGD("Could not allocate resampler context\n");
onError();
return -1;
}
if ((ret = swr_init(swr_ctx)) < 0) {
LOGD("Failed to initialize the resampling context\n");
onError();
return -1;
}
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
LOGD("Could not open audio codec: %s\n", av_err2str(ret));
onError();
return -1;
}
av_dump_format(oc, 0, dst_filename, 1);
ret = avformat_write_header(oc, NULL);
if (ret < 0) {
LOGD("Error occurred when opening output file: %s\n", av_err2str(ret));
onError();
return -1;
}
if (out_frame) {
out_frame->pts = 0;
}
}
return 0;
}
void cleanup_out() {
if (video_st) {
avcodec_close(video_st->codec);
if (dst_picture.data) {
av_free(dst_picture.data[0]);
}
if (out_frame) {
av_free(out_frame);
out_frame = NULL;
}
video_st = NULL;
}
if (audio_st) {
avcodec_close(audio_st->codec);
if (dst_samples_data) {
av_free(dst_samples_data[0]);
dst_samples_data = NULL;
}
audio_st = NULL;
}
if (fmt && !(fmt->flags & AVFMT_NOFILE)) {
avio_close(oc->pb);
fmt = NULL;
}
if (oc) {
avformat_free_context(oc);
oc = NULL;
}
if (sws_ctx) {
sws_freeContext(sws_ctx);
sws_ctx = NULL;
}
if (swr_ctx) {
swr_free(&swr_ctx);
swr_ctx = NULL;
}
current_n_out = 0;
current_in_buff = 0;
}
int write_video_frame(AVFrame *src_frame) {
int ret;
if (sws_ctx) {
ret = sws_scale(sws_ctx, (const uint8_t * const *)src_frame->data, src_frame->linesize, 0, src_frame->height, out_frame->data, out_frame->linesize);
if (ret < 0) {
LOGD("scale error: %s\n", av_err2str(ret));
onError();
return -1;
}
} else {
for (int i = 0; i < 4; i++){
out_frame->data[i] = src_frame->data[i];
out_frame->linesize[i] = src_frame->linesize[i];
}
}
AVPacket pkt = { 0 };
int got_packet;
av_init_packet(&pkt);
ret = avcodec_encode_video2(video_st->codec, &pkt, out_frame, &got_packet);
if (ret < 0) {
LOGD("Error encoding video frame: %s\n", av_err2str(ret));
onError();
return -1;
}
if (!ret && got_packet && pkt.size) {
pkt.stream_index = video_st->index;
ret = av_interleaved_write_frame(oc, &pkt);
} else {
ret = 0;
}
if (ret != 0) {
LOGD("Error while writing video frame: %s\n", av_err2str(ret));
onError();
return -1;
}
int64_t val = av_rescale_q(1, video_st->codec->time_base, video_st->time_base);
out_frame->pts += val;
return 0;
}
int check_write_packet(int flush) {
int got_packet, ret;
int writed = 0;
int dst_samples_size = av_samples_get_buffer_size(NULL, audio_st->codec->channels, audio_st->codec->frame_size, audio_st->codec->sample_fmt, 1);
while (current_n_out > audio_st->codec->frame_size || (flush && current_n_out)) {
AVFrame *frame = avcodec_alloc_frame();
AVPacket pkt2 = { 0 };
av_init_packet(&pkt2);
frame->nb_samples = min(audio_st->codec->frame_size, current_n_out);
int nb_samples_size = min(dst_samples_size, current_in_buff);
ret = avcodec_fill_audio_frame(frame, audio_st->codec->channels, audio_st->codec->sample_fmt, buff + writed, nb_samples_size, 1);
if (ret < 0) {
LOGD("Error fill frame: %s\n", av_err2str(ret));
onError();
return -1;
}
ret = avcodec_encode_audio2(audio_st->codec, &pkt2, frame, &got_packet);
if (ret < 0) {
LOGD("Error encoding audio frame: %s\n", av_err2str(ret));
onError();
return -1;
}
if (got_packet) {
pkt2.stream_index = audio_st->index;
ret = av_interleaved_write_frame(oc, &pkt2);
if (ret != 0) {
LOGD("Error while writing audio frame: %s\n", av_err2str(ret));
onError();
return -1;
}
}
writed += dst_samples_size;
current_n_out -= frame->nb_samples;
current_in_buff -= nb_samples_size;
avcodec_free_frame(&frame);
}
if (current_in_buff != 0 && writed != 0) {
memcpy(buff, buff + writed, current_in_buff);
}
return 0;
}
int write_audio_frame(AVFrame *src_frame, AVCodecContext *src_codec) {
const int n_in = src_frame->nb_samples;
double ratio = (double)audio_st->codec->sample_rate / src_frame->sample_rate;
int n_out = n_in * ratio + 32;
int64_t delay = swr_get_delay(swr_ctx, audio_st->codec->sample_rate);
if (delay > 0) {
n_out += delay;
}
if (!dst_samples_data) {
int ret = av_samples_alloc_array_and_samples(&dst_samples_data, NULL, audio_st->codec->channels, n_out, audio_st->codec->sample_fmt, 0);
if (ret < 0) {
LOGD("Could not allocate destination samples\n");
onError();
return -1;
}
}
n_out = swr_convert(swr_ctx, dst_samples_data, n_out, (const uint8_t **)src_frame->extended_data, src_frame->nb_samples);
if (n_out <= 0) {
LOGD("Error while converting\n");
onError();
return -1;
}
int total_size = av_samples_get_buffer_size(NULL, audio_st->codec->channels, n_out, audio_st->codec->sample_fmt, 1);
memcpy(buff + current_in_buff, dst_samples_data[0], total_size);
current_n_out += n_out;
current_in_buff += total_size;
return check_write_packet(0);
}
void post_video_conversion() {
check_write_packet(1);
av_write_trailer(oc);
}

View File

@ -45,12 +45,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
private native int seekOpusFile(float position);
private native int isOpusFile(String path);
private native void closeOpusFile();
private native void readOpusFile(ByteBuffer buffer, int capacity);
private native int getFinished();
private native int getSize();
private native long getPcmOffset();
private native void readOpusFile(ByteBuffer buffer, int capacity, int[] args);
private native long getTotalPcmDuration();
public static int[] readArgs = new int[3];
public static interface FileDownloadProgressListener {
public void onFailedDownload(String fileName);
@ -403,10 +401,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
}
if (buffer != null) {
readOpusFile(buffer.buffer, playerBufferSize);
buffer.size = getSize();
buffer.pcmOffset = getPcmOffset();
buffer.finished = getFinished();
readOpusFile(buffer.buffer, playerBufferSize, readArgs);
buffer.size = readArgs[0];
buffer.pcmOffset = readArgs[1];
buffer.finished = readArgs[2];
if (buffer.finished == 1) {
decodingFinished = true;
}

View File

@ -22,9 +22,9 @@ import java.util.zip.ZipFile;
public class NativeLoader {
private static final long sizes[] = new long[] {
782992, //armeabi
766628, //armeabi-v7a
1352692, //x86
795280, //armeabi
778916, //armeabi-v7a
1377300, //x86
0, //mips
};

View File

@ -94,6 +94,15 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (avatarImage != null) {
avatarImage.clearImage();
currentPhoto = null;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();

View File

@ -105,6 +105,15 @@ public class ChatBaseCell extends BaseCell {
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (avatarImage != null) {
avatarImage.clearImage();
currentPhoto = null;
}
}
private void init() {
if (backgroundDrawableIn == null) {
backgroundDrawableIn = getResources().getDrawable(R.drawable.msg_in);

View File

@ -114,6 +114,15 @@ public class ChatOrUserCell extends BaseCell {
update(0);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (avatarImage != null) {
avatarImage.clearImage();
lastAvatar = null;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), Utilities.dp(64));

View File

@ -152,6 +152,14 @@ public class DialogCell extends BaseCell {
update(0);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (avatarImage != null) {
avatarImage.clearImage();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), Utilities.dp(70));

View File

@ -176,6 +176,12 @@ public class BackupImageView extends ImageView {
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
recycleBitmap(null);
}
@Override
protected void onDraw(Canvas canvas) {
try {
@ -188,12 +194,6 @@ public class BackupImageView extends ImageView {
}
}
@Override
protected void finalize() throws Throwable {
recycleBitmap(null);
super.finalize();
}
public void setImageResourceMy(int resId) {
if (ignoreLayout) {
makeRequest = false;

View File

@ -0,0 +1,386 @@
/*
https://github.com/koral--/android-gif-drawable/
MIT License
Copyright (c)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package org.telegram.ui.Views;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.widget.MediaController;
import java.io.File;
import java.util.Locale;
public class GifDrawable extends Drawable implements Animatable, MediaController.MediaPlayerControl {
private static native void renderFrame(int[] pixels, int gifFileInPtr, int[] metaData);
private static native int openFile(int[] metaData, String filePath);
private static native void free(int gifFileInPtr);
private static native boolean reset(int gifFileInPtr);
private static native void setSpeedFactor(int gifFileInPtr, float factor);
private static native String getComment(int gifFileInPtr);
private static native int getLoopCount(int gifFileInPtr);
private static native int getDuration(int gifFileInPtr);
private static native int getCurrentPosition(int gifFileInPtr);
private static native int seekToTime(int gifFileInPtr, int pos, int[] pixels);
private static native int seekToFrame(int gifFileInPtr, int frameNr, int[] pixels);
private static native int saveRemainder(int gifFileInPtr);
private static native int restoreRemainder(int gifFileInPtr);
private static native long getAllocationByteCount(int gifFileInPtr);
private static final Handler UI_HANDLER = new Handler(Looper.getMainLooper());
private volatile int mGifInfoPtr;
private volatile boolean mIsRunning = true;
private final int[] mMetaData = new int[5];//[w, h, imageCount, errorCode, post invalidation time]
private final long mInputSourceLength;
private float mSx = 1f;
private float mSy = 1f;
private boolean mApplyTransformation;
private final Rect mDstRect = new Rect();
protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
protected final int[] mColors;
private final Runnable mResetTask = new Runnable() {
@Override
public void run() {
reset(mGifInfoPtr);
}
};
private final Runnable mStartTask = new Runnable() {
@Override
public void run() {
restoreRemainder(mGifInfoPtr);
invalidateSelf();
}
};
private final Runnable mSaveRemainderTask = new Runnable() {
@Override
public void run() {
saveRemainder(mGifInfoPtr);
}
};
private final Runnable mInvalidateTask = new Runnable() {
@Override
public void run() {
invalidateSelf();
}
};
private static void runOnUiThread(Runnable task) {
if (Looper.myLooper() == UI_HANDLER.getLooper()) {
task.run();
} else {
UI_HANDLER.post(task);
}
}
public GifDrawable(String filePath) throws Exception {
if (filePath == null) {
throw new NullPointerException("Source is null");
}
mInputSourceLength = new File(filePath).length();
mGifInfoPtr = openFile(mMetaData, filePath);
mColors = new int[mMetaData[0] * mMetaData[1]];
}
public GifDrawable(File file) throws Exception {
if (file == null) {
throw new NullPointerException("Source is null");
}
mInputSourceLength = file.length();
mGifInfoPtr = openFile(mMetaData, file.getPath());
mColors = new int[mMetaData[0] * mMetaData[1]];
}
public void recycle() {
mIsRunning = false;
int tmpPtr = mGifInfoPtr;
mGifInfoPtr = 0;
free(tmpPtr);
}
@Override
protected void finalize() throws Throwable {
try {
recycle();
} finally {
super.finalize();
}
}
@Override
public int getIntrinsicHeight() {
return mMetaData[1];
}
@Override
public int getIntrinsicWidth() {
return mMetaData[0];
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
@Override
public void start() {
mIsRunning = true;
runOnUiThread(mStartTask);
}
public void reset() {
runOnUiThread(mResetTask);
}
@Override
public void stop() {
mIsRunning = false;
runOnUiThread(mSaveRemainderTask);
}
@Override
public boolean isRunning() {
return mIsRunning;
}
public String getComment() {
return getComment(mGifInfoPtr);
}
public int getLoopCount() {
return getLoopCount(mGifInfoPtr);
}
@Override
public String toString() {
return String.format(Locale.US, "Size: %dx%d, %d frames, error: %d", mMetaData[0], mMetaData[1], mMetaData[2], mMetaData[3]);
}
public int getNumberOfFrames() {
return mMetaData[2];
}
public int getError() {
return mMetaData[3];
}
public void setSpeed(float factor) {
if (factor <= 0f) {
throw new IllegalArgumentException("Speed factor is not positive");
}
setSpeedFactor(mGifInfoPtr, factor);
}
@Override
public void pause() {
stop();
}
@Override
public int getDuration() {
return getDuration(mGifInfoPtr);
}
@Override
public int getCurrentPosition() {
return getCurrentPosition(mGifInfoPtr);
}
@Override
public void seekTo(final int position) {
if (position < 0) {
throw new IllegalArgumentException("Position is not positive");
}
runOnUiThread(new Runnable() {
@Override
public void run() {
seekToTime(mGifInfoPtr, position, mColors);
invalidateSelf();
}
});
}
public void seekToFrame(final int frameIndex) {
if (frameIndex < 0) {
throw new IllegalArgumentException("frameIndex is not positive");
}
runOnUiThread(new Runnable() {
@Override
public void run() {
seekToFrame(mGifInfoPtr, frameIndex, mColors);
invalidateSelf();
}
});
}
@Override
public boolean isPlaying() {
return mIsRunning;
}
@Override
public int getBufferPercentage() {
return 100;
}
@Override
public boolean canPause() {
return true;
}
@Override
public boolean canSeekBackward() {
return false;
}
@Override
public boolean canSeekForward() {
return getNumberOfFrames() > 1;
}
@Override
public int getAudioSessionId() {
return 0;
}
public int getFrameByteCount() {
return mMetaData[0] * mMetaData[1] * 4;
}
public long getAllocationByteCount() {
return getAllocationByteCount(mGifInfoPtr) + mColors.length * 4L;
}
public long getInputSourceByteCount() {
return mInputSourceLength;
}
public void getPixels(int[] pixels) {
if (pixels.length < mColors.length) {
throw new ArrayIndexOutOfBoundsException("Pixels array is too small. Required length: " + mColors.length);
}
System.arraycopy(mColors, 0, pixels, 0, mColors.length);
}
public int getPixel(int x, int y) {
if (x < 0) {
throw new IllegalArgumentException("x must be >= 0");
}
if (y < 0) {
throw new IllegalArgumentException("y must be >= 0");
}
if (x >= mMetaData[0]) {
throw new IllegalArgumentException("x must be < GIF width");
}
if (y >= mMetaData[1]) {
throw new IllegalArgumentException("y must be < GIF height");
}
return mColors[mMetaData[1] * y + x];
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mApplyTransformation = true;
}
@Override
public void draw(Canvas canvas) {
if (mApplyTransformation) {
mDstRect.set(getBounds());
mSx = (float) mDstRect.width() / mMetaData[0];
mSy = (float) mDstRect.height() / mMetaData[1];
mApplyTransformation = false;
}
if (mPaint.getShader() == null) {
if (mIsRunning) {
renderFrame(mColors, mGifInfoPtr, mMetaData);
} else {
mMetaData[4] = -1;
}
canvas.scale(mSx, mSy);
canvas.drawBitmap(mColors, 0, mMetaData[0], 0f, 0f, mMetaData[0], mMetaData[1], true, mPaint);
if (mMetaData[4] >= 0 && mMetaData[2] > 1) {
UI_HANDLER.postDelayed(mInvalidateTask, mMetaData[4]);
}
} else {
canvas.drawRect(mDstRect, mPaint);
}
}
public final Paint getPaint() {
return mPaint;
}
@Override
public int getAlpha() {
return mPaint.getAlpha();
}
@Override
public void setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
invalidateSelf();
}
@Override
public void setDither(boolean dither) {
mPaint.setDither(dither);
invalidateSelf();
}
@Override
public int getMinimumHeight() {
return mMetaData[1];
}
@Override
public int getMinimumWidth() {
return mMetaData[0];
}
}

View File

@ -137,6 +137,7 @@ public class ImageReceiver {
} else {
currentImage = null;
}
currentPath = null;
}
}
}
@ -160,10 +161,4 @@ public class ImageReceiver {
FileLog.e("tmessages", e);
}
}
@Override
protected void finalize() throws Throwable {
recycleBitmap(null);
super.finalize();
}
}

View File

@ -151,7 +151,7 @@
android:layout_marginTop="2dp"
android:maxLines="4"
android:minHeight="48dp"
android:textSize="18sp"
android:textSize="18dp"
android:textColorHint="#909090"
android:ems="10"
android:imeOptions="flagNoExtractUi"