package org.ebookdroid.common.bitmaps; import org.ebookdroid.common.settings.books.BookSettings; import org.ebookdroid.common.settings.definitions.AppPreferences; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Rect; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; public final class ByteBufferBitmap { private static final AtomicInteger SEQ = new AtomicInteger(); public final int id = SEQ.incrementAndGet(); final AtomicBoolean used = new AtomicBoolean(true); long gen; volatile ByteBuffer pixels; final int size; int width; int height; ByteBufferBitmap(final int width, final int height) { this.width = width; this.height = height; this.size = 4 * width * height; this.pixels = create(size).order(ByteOrder.nativeOrder()); this.pixels.rewind(); } @Override protected void finalize() throws Throwable { recycle(); } public void recycle() { ByteBuffer buf = pixels; pixels = null; free(buf); buf = null; } public static ByteBufferBitmap get(final Bitmap bmp) { if (bmp.getConfig() != Bitmap.Config.ARGB_8888) { throw new IllegalArgumentException("Wrong bitmap config: " + bmp.getConfig()); } final ByteBufferBitmap b = ByteBufferManager.getBitmap(bmp.getWidth(), bmp.getHeight()); bmp.copyPixelsToBuffer(b.pixels); return b; } public static ByteBufferBitmap get(final Bitmap bitmap, final Rect srcRect) { final ByteBufferBitmap full = get(bitmap); final int srcWidth = srcRect.width(); final int srcHeight = srcRect.height(); if (full.width == srcWidth && full.height == srcHeight) { return full; } final ByteBufferBitmap part = ByteBufferManager.getBitmap(srcWidth, srcHeight); part.copyPixelsFrom(full, srcRect.left, srcRect.top, part.width, part.height); ByteBufferManager.release(full); return part; } public void applyEffects(final BookSettings bs) { final boolean correctContrast = bs.contrast != AppPreferences.CONTRAST.defValue; final boolean correctGamma = bs.gamma != AppPreferences.GAMMA.defValue; final boolean correctExposure = bs.exposure != AppPreferences.EXPOSURE.defValue; if (correctContrast || correctGamma || correctExposure || bs.autoLevels) { if (correctGamma) { gamma(bs.gamma); } if (correctContrast) { contrast(bs.contrast); } if (correctExposure) { exposure(bs.exposure - AppPreferences.EXPOSURE.defValue); } if (bs.autoLevels) { autoLevels(); } } if (bs.tint && bs.tintColor != AppPreferences.TINT_COLOR.defValue) { tint(bs.tintColor); } } public void copyPixelsFrom(final ByteBufferBitmap src, final int left, final int top, final int width, final int height) { if (width > this.width) { throw new IllegalArgumentException("width > this.width: " + width + ", " + this.width); } if (height > this.height) { throw new IllegalArgumentException("height > this.height: " + height + ", " + this.height); } if (left + width > src.width) { throw new IllegalArgumentException("left + width > src.width: " + left + ", " + width + ", " + src.width); } if (top + height > src.height) { throw new IllegalArgumentException("top + height > src.height: " + top + ", " + height + ", " + src.height); } nativeFillRect(src.pixels, src.width, this.pixels, this.width, left, top, width, height); } public ByteBuffer getPixels() { return pixels; } public int getWidth() { return width; } public int getHeight() { return height; } public IBitmapRef toBitmap() { final IBitmapRef bitmap = BitmapManager.getBitmap("RawBitmap", width, height, Bitmap.Config.ARGB_8888); pixels.rewind(); bitmap.getBitmap().copyPixelsFromBuffer(pixels); return bitmap; } public void fillAlpha(final int v) { nativeFillAlpha(pixels, width, height, v); } public void invert() { nativeInvert(pixels, width, height); } public void tint(int c) { nativeTint(pixels, width, height, c); } public int getAvgLum() { return nativeAvgLum(pixels, width, height); } public void contrast(final int contrast) { nativeContrast(pixels, width, height, contrast * 256 / 100); } public void gamma(final int gamma) { nativeGamma(pixels, width, height, gamma); } public void exposure(final int exposure) { nativeExposure(pixels, width, height, exposure * 128 / 100); } public void autoLevels() { nativeAutoLevels2(pixels, width, height); } public void eraseColor(final int color) { nativeEraseColor(pixels, width, height, color); } @Override public String toString() { return getClass().getSimpleName() + "[id=" + id + ", width=" + width + ", height=" + height + ", size=" + size + "]"; } private static native void nativeInvert(ByteBuffer src, int width, int height); private static native void nativeTint(ByteBuffer src, int width, int height, int color); private static native void nativeFillAlpha(ByteBuffer src, int width, int height, int value); private static native void nativeEraseColor(ByteBuffer src, int width, int height, int color); /* contrast value 256 - normal */ private static native void nativeContrast(ByteBuffer src, int width, int height, int contrast); /* gamma value 100 - normal */ private static native void nativeGamma(ByteBuffer src, int width, int height, int gamma); /* Exposure correction values -128...+128 */ private static native void nativeExposure(ByteBuffer src, int width, int height, int exposure); private static native void nativeAutoLevels(ByteBuffer src, int width, int height); private static native void nativeAutoLevels2(ByteBuffer src, int width, int height); private static native int nativeAvgLum(ByteBuffer src, int width, int height); private static native void nativeFillRect(ByteBuffer src, int srcWidth, ByteBuffer dst, int dstWidth, int x, int y, int width, int height); private static native ByteBuffer create(int size); private static native void free(ByteBuffer buf); }