code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hippo.image;

/*
 * Created by Hippo on 9/12/2016.
 */

import android.graphics.Bitmap;
import android.graphics.Rect;
import android.support.annotation.Keep;
import android.support.annotation.Nullable;
import android.util.Log;

import java.io.InputStream;

public final class BitmapRegionDecoder {

    private static final String LOG_TAG = BitmapRegionDecoder.class.getSimpleName();

    private long mNativePtr;
    private final int mWidth;
    private final int mHeight;
    private final int mFormat;
    private final boolean mOpaque;

    private final Object mNativeLock = new Object();

    @Keep
    private BitmapRegionDecoder(long nativePtr, int width, int height,
            int format, boolean opaque) {
        mNativePtr = nativePtr;
        mWidth = width;
        mHeight = height;
        mFormat = format;
        mOpaque = opaque;
    }

    /**
     * Return width of the original image.
     */
    public int getWidth() {
        return mWidth;
    }

    /**
     * Return height of the original image.
     */
    public int getHeight() {
        return mHeight;
    }

    /**
     * Return format of the original image.
     */
    public int getFormat() {
        return mFormat;
    }

    /**
     * Return {@code true} if the original image is opaque.
     */
    public boolean isOpaque() {
        return mOpaque;
    }

    /**
     * config is {@code BitmapDecoder.CONFIG_AUTO}.
     * ratio is {@code 1}.
     *
     * @see #decodeRegion(Rect, int, int)
     */
    @Nullable
    public Bitmap decodeRegion(Rect rect) {
        return decodeRegion(rect, BitmapDecoder.CONFIG_AUTO, 1);
    }

    /**
     * ratio is {@code 1}.
     *
     * @see #decodeRegion(Rect, int, int)
     */
    @Nullable
    public Bitmap decodeRegion(Rect rect, @BitmapDecoder.Config int config) {
        return decodeRegion(rect, config, 1);
    }

    /**
     * Decodes a rectangle region in the image specified by rect.
     *
     * @param rect The rectangle that specified the region to be decode.
     *             Null for decode full image.
     * @param config One of {@link BitmapDecoder#CONFIG_AUTO}, {@link BitmapDecoder#CONFIG_RGB_565}
     *               and {@link BitmapDecoder#CONFIG_RGBA_8888}
     * @param ratio If set to a value > 1, requests the decoder to subsample the original.
     *               image, returning a smaller image to save memory. Power of 2 is not necessary.
     * @return The decoded bitmap, or null if the image data could not be
     *         decoded.
     */
    @Nullable
    public Bitmap decodeRegion(Rect rect, @BitmapDecoder.Config int config, int ratio) {
        synchronized (mNativeLock) {
            if (mNativePtr == 0) {
                Log.e(LOG_TAG, "This region decoder is recycled.");
                return null;
            }

            // Resolve CONFIG_AUTO
            if (config == BitmapDecoder.CONFIG_AUTO) {
                config = mOpaque ? BitmapDecoder.CONFIG_RGB_565 : BitmapDecoder.CONFIG_RGBA_8888;
            }

            if (rect == null || (rect.left == 0 && rect.top == 0 && rect.right == mWidth && rect.bottom == mHeight)) {
                // Requested full image, decode without regions
                return nativeDecodeRegion(mNativePtr, 0, 0, 0, 0, config, ratio);
            } else {
                if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= mWidth || rect.top >= mHeight || rect.isEmpty()) {
                    Log.e(LOG_TAG, "The decode rect is invalid.");
                    return null;
                } else {
                    return nativeDecodeRegion(mNativePtr, rect.left, rect.top, rect.width(), rect.height(), config, ratio);
                }
            }
        }
    }

    /**
     * Frees up the memory associated with this region decoder.
     * It will return null if decodeRegion().
     */
    public void recycle() {
        synchronized (mNativeLock) {
            if (mNativePtr != 0) {
                nativeRecycle(mNativePtr);
                mNativePtr = 0;
            }
        }
    }

    /**
     * Returns true if this region decoder has been recycled.
     */
    public final boolean isRecycled() {
        return mNativePtr == 0;
    }

    /**
     * Create a BitmapRegionDecoder from the {@code InputStream}.
     * BitmapRegionDecoder will make a copy of the data in the
     * InputStream, and the InputStream will be closed.
     */
    @Nullable
    public static BitmapRegionDecoder newInstance(InputStream is) {
        if (is == null) {
            return null;
        }
        return nativeNewInstance(is);
    }

    static {
        System.loadLibrary("image");
    }

    private static native BitmapRegionDecoder nativeNewInstance(InputStream is);

    private static native Bitmap nativeDecodeRegion(long nativePtr, int x, int y, int width, int height, int config, int ratio);

    private static native void nativeRecycle(long nativePtr);
}


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hippo.image;

/*
 * Created by Hippo on 8/4/2016.
 */

import android.graphics.Bitmap;
import android.support.annotation.NonNull;

import java.io.InputStream;

public final class Image {

    /**
     * Unknown image format
     */
    public static final int FORMAT_UNKNOWN = -1;

    /**
     * Plain image format, for {@code Image} from {@link #create(Bitmap)}
     */
    public static final int FORMAT_PLAIN = 0;

    /**
     * Bmp image format
     */
    public static final int FORMAT_BMP = 1;

    /**
     * JPEG image format
     */
    public static final int FORMAT_JPEG = 2;

    /**
     * PNG image format
     */
    public static final int FORMAT_PNG = 3;

    /**
     * GIF image format
     */
    public static final int FORMAT_GIF = 4;

    /**
     * WEBP image format
     */
    public static final int FORMAT_WEBP = 5;

    private static long mBuffer = 0;
    private static int mBufferSize;

    static int mImageDataCount;
    static int mImageRendererCount;

    public static ImageData decode(@NonNull InputStream is) {
        return decode(is, false);
    }

    public static ImageData decode(@NonNull InputStream is, boolean partially) {
        return nativeDecode(is, partially);
    }

    public static ImageData create(@NonNull Bitmap bitmap) {
        return nativeCreate(bitmap);
    }

    /**
     * Actual buffer byte count = size * 4
     */
    public static void createBuffer(int size) {
        if (mBuffer == 0) {
            mBuffer = nativeCreateBuffer(size);
            mBufferSize = size;
        }
    }

    public static void destroyBuffer() {
        if (mBuffer != 0) {
            nativeDestroyBuffer(mBuffer);
            mBuffer = 0;
        }
    }

    static long getBufferPtr(int size) {
        if (mBuffer == 0) {
            throw new IllegalStateException("Please call createBuffer first");
        }
        if (size > mBufferSize) {
            throw new IllegalStateException("Requested buffer size can't be more than allocated buffer size");
        }
        return mBuffer;
    }

    /**
     * Return all supported image formats, exclude {@link #FORMAT_PLAIN} and {@link #FORMAT_UNKNOWN}
     */
    public static int[] getSupportedImageFormats() {
        return nativeGetSupportedImageFormats();
    }

    /**
     * Return decoder description of the image format,
     * {@code null} for invalid image format.
     */
    public static String getLibraryDescription(int format) {
        return nativeGetLibraryDescription(format);
    }

    /**
     * Return the number of all ImageData.
     */
    public static int getImageDataCount() {
        return mImageDataCount;
    }

    /**
     * Return the number of all ImageRenderer.
     */
    public static int getImageRendererCount() {
        return mImageRendererCount;
    }

    static {
        System.loadLibrary("image");
    }

    private static native ImageData nativeDecode(InputStream is, boolean partially);

    private static native ImageData nativeCreate(Bitmap bitmap);

    private static native long nativeCreateBuffer(int size);

    private static native void nativeDestroyBuffer(long buffer);

    private static native int[] nativeGetSupportedImageFormats();

    private static native String nativeGetLibraryDescription(int format);
}

repo_name: tachiyomiorg/mirror

license: mit


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hippo.image;

/*
 * Created by Hippo on 8/4/2016.
 */

import android.support.annotation.NonNull;

public interface ImageData {

    /**
     * Recycle the ImageData, it can not be used anymore,
     * the resources associated with it will be released.
     * Call {@link #isReferenced()} to make sure the ImageData
     * is not referenced, or throw IllegalStateException.
     *

     * In default, {@code recycle()} will be called when the
     * ImageData changed from referenced to non-referenced.
     * If you want to use this feature, make sure each ImageData
     * added a reference after created, and all referenced removed
     * when it is useless.
     *
     * @see #isReferenced()
     * @see #setAutomatic(boolean)
     */
    void recycle();

    /**
     * Return true if the ImageData is recycled.
     */
    boolean isRecycled();

    /**
     * Whether ImageData will recycle itself when it's not referenced.
     *

     * Recycling action will only be triggered when the ImageData changed
     * from referenced to non-referenced.
     */
    void setAutomatic(boolean automatic);

    /**
     * Return true if ImageData will recycle itself when it's not referenced.
     * Return true in default.
     *
     * @see #setAutomatic(boolean)
     */
    boolean isAutomatic();

    /**
     * Create a ImageRenderer to render the ImageData.
     * It will add reference to ImageData.
     *
     * @see #addReference()
     */
    @NonNull
    ImageRenderer createImageRenderer();

    /**
     * Check whether the ImageData is referenced.
     * It is safe to release if it is not referenced.
     *
     * @see #addReference()
     * @see #removeReference()
     */
    boolean isReferenced();

    /**
     * Add reference to the ImageData.
     * ImageData can only be recycled when
     * it is not referenced.
     *
     * @see #isReferenced()
     * @see #removeReference()
     */
    void addReference();

    /**
     * Remove reference from the ImageData.
     * Throw IllegalStateException if the Image is not
     * referenced before {@code removeReference()} called.
     *
     * @see #isReferenced()
     * @see #addReference()
     */
    void removeReference();

    /**
     * Complete the image decoding. It will be ignored if the
     * image is totally decoded.
     */
    void complete();

    /**
     * Check whether the image is totally decoded.
     */
    boolean isCompleted();

    /**
     * Return the width of the image.
     */
    int getWidth();

    /**
     * Return the height of the image.
     */
    int getHeight();

    /**
     * Return the format of the image.
     *
     * @see Image#FORMAT_UNKNOWN
     * @see Image#FORMAT_BMP
     * @see Image#FORMAT_JPEG
     * @see Image#FORMAT_PNG
     * @see Image#FORMAT_GIF
     */
    int getFormat();

    /**
     * Return true is the image is opaque.
     */
    boolean isOpaque();

    /**
     * Return the frame count of the image.
     * Return 0 if it is not a animated image.
     *

     * It should not be called when not completed
     * or throw IllegalStateException
     */
    int getFrameCount();

    /**
     * Return the delay in millisecond.
     * Return {@link Integer#MAX_VALUE} if it is not a animated image.
     *

     * It should not be called when not completed
     * or throw IllegalStateException
     */
    int getDelay(int frame);

    /**
     * Get the memory size to store the ImageData.
     *

     * It should not be called when not completed
     * or throw IllegalStateException
     */
    int getByteCount();

    /**
     * If BrowserCompat is enabled, delay than equal or smaller than
     * 10 ms will be 100 ms. For static image, do noting.
     */
    void setBrowserCompat(boolean enable);

    /**
     * Return whether BrowserCompat enabled. For static image,
     * always return true.
     */
    boolean isBrowserCompat();
}


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hippo.image;

/*
 * Created by Hippo on 9/12/2016.
 */

import android.support.annotation.Keep;

@Keep
public final class ImageInfo {

    public int width;
    public int height;
    public int format;
    public boolean opaque;
    /**
     * -1 for unknown
     */
    public int frameCount;

    // For native code
    private void set(int width, int height, int format, boolean opaque, int frameCount) {
        this.width = width;
        this.height = height;
        this.format = format;
        this.opaque = opaque;
        this.frameCount = frameCount;
    }
}


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hippo.image;

/*
 * Created by Hippo on 8/4/2016.
 */

import android.graphics.Bitmap;
import android.support.annotation.NonNull;

public interface ImageRenderer {

    /**
     * Recycle the ImageRenderer, it can not be used anymore,
     * the resources associated with it will be released.
     * It will remove reference from its ImageData.
     *
     * @see ImageData#removeReference()
     */
    void recycle();

    /**
     * Return true if the ImageRenderer is recycled.
     */
    boolean isRecycled();

    /**
     * Return the ImageData associated with this ImageRenderer.
     * It will not return null, but the ImageData might be recycled.
     */
    @NonNull
    ImageData getImageData();

    /**
     * Return delay of current delay.
     * Return 0 if current frame is invalid.
     */
    int getCurrentDelay();

    /**
     * Render image to Bitmap.
     *
     * @param width src width
     * @param height src height
     * @param ratio dst width = src width / ratio
     */
    void render(Bitmap bitmap, int dstX, int dstY, int srcX, int srcY,
            int width, int height, int ratio, boolean fillBlank, int fillColor);

    /**
     * Call glTexImage2D or glTexSubImage2D.
     *
     * @param width src width
     * @param height src height
     * @param ratio dst width = src width / ratio
     */
    void glTex(boolean init, int texW, int texH, int dstX, int dstY,
            int srcX, int srcY, int width, int height, int ratio);

    /**
     * Advance current frame.
     *

     * It should not be called when not completed
     * or throw IllegalStateException.
     */
    void advance();

    /**
     * Set current frame to the first one.
     *

     * It should not be called when not completed
     * or throw IllegalStateException.
     */
    void reset();
}


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hippo.image;

/*
 * Created by Hippo on 8/5/2016.
 */

import android.graphics.Bitmap;
import android.support.annotation.NonNull;

final class StaticDelegateImage implements ImageRenderer {

    private final StaticImage mStaticImage;
    private boolean mReleased = false;

    StaticDelegateImage(StaticImage staticImage) {
        mStaticImage = staticImage;

        // For statistics
        ++Image.mImageRendererCount;
    }

    @Override
    public void recycle() {
        if (!mReleased) {
            mReleased = true;
            mStaticImage.removeReference();

            // For statistics
            --Image.mImageRendererCount;
        }
    }

    @Override
    public boolean isRecycled() {
        return mReleased;
    }

    @NonNull
    @Override
    public ImageData getImageData() {
        return mStaticImage;
    }

    @Override
    public int getCurrentDelay() {
        return Integer.MAX_VALUE;
    }

    // Throw IllegalStateException if recycled
    private void checkRecycled(String errorMessage) {
        if (mReleased) {
            throw new IllegalStateException(errorMessage);
        }
    }

    @Override
    public void render(Bitmap bitmap, int dstX, int dstY, int srcX, int srcY,
            int width, int height, int ratio, boolean fillBlank, int fillColor) {
        checkRecycled("Can't call render on recycled ImageRender");
        nativeRender(mStaticImage.getNativePtr(), bitmap, dstX, dstY,
                srcX, srcY, width, height, ratio, fillBlank, fillColor);
    }

    @Override
    public void glTex(boolean init, int texW, int texH, int dstX, int dstY,
            int srcX, int srcY, int width, int height, int ratio) {
        checkRecycled("Can't glTex render on recycled ImageRender");
        nativeGlTex(mStaticImage.getNativePtr(), Image.getBufferPtr(texW * texH),
                init, texW, texH, dstX, dstY, srcX, srcY, width, height, ratio);
    }

    @Override
    public void advance() {
        // Empty, nothing to do for StaticDelegateImage.
    }

    @Override
    public void reset() {
        // Empty, nothing to do for StaticDelegateImage.
    }

    private static native void nativeRender(long nativePtr,
            Bitmap bitmap, int dstX, int dstY, int srcX, int srcY,
            int width, int height, int ratio, boolean fillBlank, int fillColor);

    private static native void nativeGlTex(long nativePtr, long bufferPtr,
            boolean init, int texW, int texH, int dstX, int dstY,
            int srcX, int srcY, int width, int height, int ratio);
}


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hippo.image;

/*
 * Created by Hippo on 8/4/2016.
 */

import android.support.annotation.Keep;
import android.support.annotation.NonNull;

@Keep
final class StaticImage implements ImageData {

    private long mNativePtr;
    private final int mWidth;
    private final int mHeight;
    private final int mFormat;
    private final boolean mOpaque;
    private final int mByteCount;

    private int mReference;
    private boolean mAutomatic = true;

    private StaticImage(long nativePtr, int width, int height,
            int format, boolean opaque, int byteCount) {
        mNativePtr = nativePtr;
        mWidth = width;
        mHeight = height;
        mFormat = format;
        mOpaque = opaque;
        mByteCount = byteCount;

        // For statistics
        ++Image.mImageDataCount;
    }

    @Override
    public void recycle() {
        // Check reference
        if (mReference > 0) {
            throw new IllegalStateException("Can't release referenced ImageData");
        }
        // Release native data
        if (mNativePtr != 0) {
            nativeRecycle(mNativePtr);
            mNativePtr = 0;

            // For statistics
            --Image.mImageDataCount;
        }
    }

    @Override
    public boolean isRecycled() {
        return mNativePtr == 0;
    }

    @Override
    public void setAutomatic(boolean automatic) {
        mAutomatic = automatic;
    }

    @Override
    public boolean isAutomatic() {
        return mAutomatic;
    }

    // Throw IllegalStateException if recycled
    private void checkRecycled(String errorMessage) {
        if (mNativePtr == 0) {
            throw new IllegalStateException(errorMessage);
        }
    }

    // Return native ptr
    long getNativePtr() {
        checkRecycled("Can't use recycled ImageData");
        return mNativePtr;
    }

    @NonNull
    @Override
    public ImageRenderer createImageRenderer() {
        checkRecycled("Can't create ImageRenderer on recycled ImageData");
        ++mReference;
        return new StaticDelegateImage(this);
    }

    @Override
    public boolean isReferenced() {
        return mReference != 0;
    }

    @Override
    public void addReference() {
        ++mReference;
    }

    @Override
    public void removeReference() {
        --mReference;
        // Check reference valid
        if (mReference < 0) {
            throw new IllegalStateException("Can't remove reference from a non-referenced ImageData.");
        }
        // Check automatic
        if (mReference == 0 && mAutomatic) {
            recycle();
        }
    }

    @Override
    public void complete() {
        // Empty, nothing to do for StaticImage.
    }

    @Override
    public boolean isCompleted() {
        // StaticImage is always completed.
        return true;
    }

    @Override
    public int getWidth() {
        return mWidth;
    }

    @Override
    public int getHeight() {
        return mHeight;
    }

    @Override
    public int getFormat() {
        return mFormat;
    }

    @Override
    public boolean isOpaque() {
        return mOpaque;
    }

    @Override
    public int getFrameCount() {
        // Frame count of StaticImage is always 1.
        return 1;
    }

    @Override
    public int getDelay(int frame) {
        return Integer.MAX_VALUE;
    }

    @Override
    public int getByteCount() {
        return mByteCount;
    }

    @Override
    public void setBrowserCompat(boolean enable) {}

    @Override
    public boolean isBrowserCompat() {
        return true;
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            if (mNativePtr != 0) {
                nativeRecycle(mNativePtr);
                mNativePtr = 0;
            }
        } finally {
            super.finalize();
        }
    }

    private static native void nativeRecycle(long nativePtr);
}


code:
# Copyright 2018 Hippo Seven
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

cmake_minimum_required(VERSION 3.4.1)
project(root)

OPTION(IMAGE_SINGLE_SHARED_LIB "Compile image as a single shared library with all codecs included" FALSE)

add_subdirectory(cpufeatures)
add_subdirectory(image)
add_subdirectory(jpeg)
add_subdirectory(png)
add_subdirectory(gif)
add_subdirectory(webp)
add_subdirectory(test)

unset(IMAGE_SINGLE_SHARED_LIB CACHE)


code:
# Copyright 2018 Hippo Seven
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

cmake_minimum_required(VERSION 3.4.1)
project(cpufeatures C)

add_library(cpufeatures STATIC ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)


code:
# Copyright 2018 Hippo Seven
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

cmake_minimum_required(VERSION 3.4.1)
project(image-gif C)

set(GIFLIB_SOURCES
    image_gif.c
    giflib/lib/dgif_lib.c
    giflib/lib/gif_hash.c
    giflib/lib/gifalloc.c
    giflib/lib/openbsd-reallocarray.c
)

set(GIFLIB_INCLUDES
    ${IMAGE_EXPORT_INCLUDES}
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/giflib/lib
)

set(GIFLIB_DEFINITIONS
)

set(GIFLIB_LIBRARIES
)

if(IMAGE_SINGLE_SHARED_LIB)
    # Compile a static library for image-singlelib
    set(GIFLIB_LIBRARY_NAME image-gif-static)
    set(GIFLIB_LIBRARY_TYPE STATIC)
    set(GIFLIB_INCLUDES_VISIBILITY PUBLIC) # Share include dir with image project
else()
    # Compile a shared library to be imported by image-core
    set(GIFLIB_LIBRARY_NAME image-gif)
    set(GIFLIB_LIBRARY_TYPE SHARED)
    set(GIFLIB_INCLUDES_VISIBILITY PRIVATE)

    # Link against image shared lib to get helper methods
    set(GIFLIB_LIBRARIES
        ${GIFLIB_LIBRARIES}
        image
        log
    )
endif()

add_library(${GIFLIB_LIBRARY_NAME} ${GIFLIB_LIBRARY_TYPE} ${GIFLIB_SOURCES})
target_include_directories(${GIFLIB_LIBRARY_NAME} ${GIFLIB_INCLUDES_VISIBILITY} ${GIFLIB_INCLUDES})
target_compile_definitions(${GIFLIB_LIBRARY_NAME} PRIVATE ${GIFLIB_DEFINITIONS})
target_link_libraries(${GIFLIB_LIBRARY_NAME} PRIVATE ${GIFLIB_LIBRARIES})


code:
/*
 * Copyright 2015 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>

#include "image.h"
#include "image_gif.h"
#include "../log.h"

#define IMAGE_GIF_PREPARE_NONE 0x00
#define IMAGE_GIF_PREPARE_BACKGROUND 0x01
#define IMAGE_GIF_PREPARE_USE_BACKUP 0x02

typedef struct {
  int tran;
  int disposal;
  int delay;
  int prepare;
} GifFrame;

typedef struct {
  GifFileType* gif_file;
  GifFrame* frames;
  Stream* stream;
} GifData;

typedef struct {
  unsigned char red;
  unsigned char green;
  unsigned char blue;
  unsigned char alpha;
} RGBA;

static int error_code = 0;

LIBRARY_EXPORT
bool gif_init(ImageLibrary* library) {
  library->loaded = true;
  library->is_magic = gif_is_magic;
  library->decode = gif_decode;
  library->decode_info = gif_decode_info;
  library->decode_buffer = NULL;
  library->create = NULL;
  library->get_description = gif_get_description;

  return true;
}

bool gif_is_magic(Stream* stream) {
  uint8_t magic[2];

  size_t read = stream->peek(stream, magic, sizeof(magic));
  if (read != sizeof(magic)) {
    LOGE(MSG("Could not read %zu bytes from stream, only read %zu"), sizeof(magic), read);
    return false;
  }

  return magic[0] == IMAGE_GIF_MAGIC_NUMBER_0 && magic[1] == IMAGE_GIF_MAGIC_NUMBER_1;
}

const char* gif_get_description() {
  return IMAGE_GIF_DECODER_DESCRIPTION;
}

static int custom_read_fun(GifFileType* gif, GifByteType* bytes, int size) {
  Stream* stream = gif->UserData;
  return (int) stream->read(stream, bytes, (size_t) size);
}

static void read_gcb(GifFileType* gif_file, int index, GifFrame* frame, GifFrame* pre_frame) {
  GraphicsControlBlock gcb;
  int pre_disposal;

  // Read gcb
  if (DGifSavedExtensionToGCB(gif_file, index, &gcb) == GIF_OK) {
    frame->tran = gcb.TransparentColor;
    frame->delay = gcb.DelayTime * 10;
    frame->disposal = gcb.DisposalMode;
  } else {
    frame->tran = -1;
    frame->delay = 0;
    frame->disposal = DISPOSE_DO_NOT;
  }

  // Set pop
  pre_disposal = pre_frame != NULL ? pre_frame->disposal : DISPOSE_BACKGROUND;
  switch (pre_disposal) {
    case DISPOSAL_UNSPECIFIED:
    case DISPOSE_DO_NOT:
    default:
      frame->prepare = IMAGE_GIF_PREPARE_NONE;
      break;
    case DISPOSE_BACKGROUND:
      frame->prepare = IMAGE_GIF_PREPARE_BACKGROUND;
      break;
    case DISPOSE_PREVIOUS:
      frame->prepare = IMAGE_GIF_PREPARE_USE_BACKUP;
      break;
  }
}

static bool get_color_from_table(const ColorMapObject* cmap, int index, RGBA* color) {
  if (cmap == NULL || index < 0 || index >= cmap->ColorCount) {
    return false;
  } else {
    GifColorType gct = cmap->Colors[index];
    color->red = gct.Red;
    color->green = gct.Green;
    color->blue = gct.Blue;
    color->alpha = 0xff;
    return true;
  }
}

static void clear(RGBA* pixels, int num, RGBA color) {
  RGBA* ptr = pixels;
  int i;
  for (i = 0; i < num; i++) {
    *(ptr++) = color;
  }
}

static void clear_bg(GifFileType* gif_file, void* pixels) {
  RGBA color;
  if (!get_color_from_table(gif_file->SColorMap, gif_file->SBackGroundColor, &color)) {
    color.red = 0x00;
    color.green = 0x00;
    color.blue = 0x00;
    color.alpha = 0x00;
  }
  clear(pixels, gif_file->SWidth * gif_file->SHeight, color);
}

static void copy_line(GifByteType* src, RGBA* dst, const ColorMapObject* cmap, int tran, size_t len) {
  for (; len > 0; len--, src++, dst++) {
    int index = *src;
    if (tran == -1 || index != tran) {
      get_color_from_table(cmap, index, dst);
    }
  }
}

static void blend(GifFileType* gif_file, int index, void* pixels, int tran) {
  int width = gif_file->SWidth;
  int height = gif_file->SHeight;
  SavedImage* cur = gif_file->SavedImages + index;
  GifImageDesc desc = cur->ImageDesc;
  int copy_width = MIN(width - desc.Left, desc.Width);
  int copy_height = MIN(height - desc.Top, desc.Height);
  ColorMapObject *cmap = desc.ColorMap;
  GifByteType* src = cur->RasterBits;
  RGBA* dst = pixels;
  GifByteType* src_ptr;
  RGBA* dst_ptr;
  size_t len;
  int i;

  if (cmap == NULL) {
    cmap = gif_file->SColorMap;
  }
  if (cmap != NULL) {
    for (i = 0; i < copy_height; i++) {
      src_ptr = src + (i * desc.Width);
      dst_ptr = dst + ((desc.Top + i) * width + desc.Left);
      len = (size_t) copy_width;
      copy_line(src_ptr, dst_ptr, cmap, tran, len);
    }
  } else {
    LOGW(MSG("Can't find color map"));
  }
}

static void free_last_frame(GifFileType* gif_file) {
  SavedImage* last_image = gif_file->SavedImages + gif_file->ImageCount;

  if (last_image->ImageDesc.ColorMap != NULL) {
    GifFreeMapObject(last_image->ImageDesc.ColorMap);
    last_image->ImageDesc.ColorMap = NULL;
  }

  if (last_image->RasterBits != NULL) {
    free(last_image->RasterBits);
    last_image->RasterBits = NULL;
  }

  GifFreeExtensions(&last_image->ExtensionBlockCount, &last_image->ExtensionBlocks);

  --gif_file->ImageCount;
}

// Remove last frame if it is invalid
static void fix_gif_file(GifFileType* gif_file) {
  if (gif_file->ImageCount == 0) {
    return;
  }

  // If the last RasterBits is NULL, discard it
  if ((gif_file->SavedImages + (gif_file->ImageCount - 1))->RasterBits == NULL) {
    free_last_frame(gif_file);
  }
}

static Stream* get_stream(AnimatedImage* image) {
  return ((GifData*) image->data)->stream;
}

static void complete(AnimatedImage* image) {
  GifData* data = image->data;
  GifFrame* frames;
  uint32_t i;

  if (image->completed || data->gif_file == NULL || data->stream == NULL) {
    return;
  }

  if (DGifSlurp(data->gif_file) == GIF_ERROR) {
    fix_gif_file(data->gif_file);
  }

  // Close stream
  data->stream->close(&data->stream);

  frames = realloc(data->frames, data->gif_file->ImageCount * sizeof(GifFrame));
  if (frames != NULL) {
    // Read gcb
    for (i = 0; i < data->gif_file->ImageCount; i++) {
      read_gcb(data->gif_file, i, frames + i, i == 0 ? NULL : frames + (i - 1));
    }
    // Assign
    data->frames = frames;
  } else {
    WTF_OOM;
    // Make it only one frame
    while (data->gif_file->ImageCount > 1) {
      free_last_frame(data->gif_file);
    }
    data->frames->delay = INT32_MAX;
  }

  // Clean up
  data->stream = NULL;

  // Completed
  image->completed = true;
}

static uint32_t get_frame_count(AnimatedImage* image) {
  return (uint32_t) ((GifData*)image->data)->gif_file->ImageCount;
}

static uint32_t get_delay(AnimatedImage* image, uint32_t frame) {
  GifData* data = image->data;
  if (frame >= data->gif_file->ImageCount) {
    LOGE(MSG("Frame count is %ud, can't get delay of index %ud"), data->gif_file->ImageCount, frame);
    return 0;
  }
  return (uint32_t) (data->frames + frame)->delay;
}

static uint32_t get_byte_count(AnimatedImage* image) {
  GifFileType* gif_file = ((GifData*) image->data)->gif_file;
  SavedImage* saved_image;
  uint32_t size = 0;
  uint32_t i;
  if (gif_file->SavedImages != NULL) {
    for (i = 0; i < gif_file->ImageCount; i++) {
      saved_image = gif_file->SavedImages + i;
      size += saved_image->ImageDesc.Width * saved_image->ImageDesc.Height;
    }
  }
  return size;
}

static void advance(AnimatedImage* image, DelegateImage* dImage) {
  GifData* data = image->data;
  GifFrame* frame;
  GifFileType* gif_file = data->gif_file;
  int32_t target_index = dImage->index + 1;

  if (target_index < 0 || target_index >= gif_file->ImageCount) {
    target_index = 0;
  }
  if (target_index == dImage->index) {
    return;
  }

  frame = data->frames + target_index;

  if (frame->disposal == DISPOSE_PREVIOUS && frame->prepare == IMAGE_GIF_PREPARE_USE_BACKUP) {
    delegate_image_switch_data_backup(dImage);
  } else {
    // Backup
    if (frame->disposal == DISPOSE_PREVIOUS) {
      delegate_image_backup(dImage);
    }

    // Prepare
    switch (frame->prepare) {
      case IMAGE_GIF_PREPARE_NONE:
        // Do nothing
        break;
      default:
      case IMAGE_GIF_PREPARE_BACKGROUND:
        clear_bg(gif_file, dImage->buffer);
        break;
      case IMAGE_GIF_PREPARE_USE_BACKUP:
        delegate_image_restore(dImage);
        break;
    }
  }

  blend(gif_file, target_index, dImage->buffer, frame->tran);

  delegate_image_apply(dImage);

  dImage->index = target_index;
}

static void recycle(AnimatedImage** image) {
  GifData* data;

  if (image == NULL || *image == NULL) {
    return;
  }

  data = (*image)->data;

  DGifCloseFile(data->gif_file, &error_code);
  data->gif_file = NULL;

  free(data->frames);
  data->frames = NULL;

  if (data->stream != NULL) {
    data->stream->close(&data->stream);
    data->stream = NULL;
  }

  free(data);
  (*image)->data = NULL;

  free(*image);
  *image = NULL;
}

AnimatedImage* gif_decode(Stream* stream, bool partially, bool* animated) {
  *animated = true;

  AnimatedImage* animated_image = NULL;
  GifData* gif_data = NULL;
  GifFrame* frames = NULL;
  GifFileType* gif_file = NULL;
  int i;

  animated_image = malloc(sizeof(AnimatedImage));
  gif_data = malloc(sizeof(GifData));
  if (animated_image == NULL || gif_data == NULL) {
    WTF_OOM;
    free(animated_image);
    free(gif_data);
    return NULL;
  }

  // Open
  gif_file = DGifOpen(stream, &custom_read_fun, &error_code);
  if (gif_file == NULL) {
    WTF_OOM;
    free(animated_image);
    free(gif_data);
    return NULL;
  }

  if (partially) {
    // Glance
    if (DGifGlance(gif_file) != GIF_OK) {
      LOGE(MSG("GIF error code %d"), error_code);
      DGifCloseFile(gif_file, &error_code);
      free(animated_image);
      free(gif_data);
      return NULL;
    }

    // Frame info
    frames = malloc(sizeof(GifFrame));
    if (frames == NULL) {
      WTF_OOM;
      DGifCloseFile(gif_file, &error_code);
      free(animated_image);
      free(gif_data);
      return NULL;
    }

    // Read gcb
    read_gcb(gif_file, 0, frames, NULL);
  } else {
    // Slurp
    if (DGifSlurp(gif_file) == GIF_ERROR) {
      fix_gif_file(gif_file);
    }
    if (gif_file->ImageCount <= 0) {
      LOGE(MSG("No frame"));
      DGifCloseFile(gif_file, &error_code);
      free(animated_image);
      free(gif_data);
      return NULL;
    }

    // Frame info
    frames = malloc(gif_file->ImageCount * sizeof(GifFrame));
    if (frames == NULL) {
      WTF_OOM;
      DGifCloseFile(gif_file, &error_code);
      free(animated_image);
      free(gif_data);
      return NULL;
    }

    // Read gcb
    for (i = 0; i < gif_file->ImageCount; i++) {
      read_gcb(gif_file, i, frames + i, i == 0 ? NULL : frames + (i - 1));
    }
  }

  gif_data->gif_file = gif_file;
  gif_data->frames = frames;
  gif_data-> stream = partially ? stream : NULL;

  animated_image->width = (uint32_t) gif_file->SWidth;
  animated_image->height = (uint32_t) gif_file->SHeight;
  animated_image->format = IMAGE_FORMAT_GIF;
  animated_image->opaque = frames->tran < 0;
  animated_image->completed = !partially;
  animated_image->data = gif_data;

  animated_image->get_stream = &get_stream;
  animated_image->complete = &complete;
  animated_image->get_frame_count = &get_frame_count;
  animated_image->get_delay = &get_delay;
  animated_image->get_byte_count = &get_byte_count;
  animated_image->advance = &advance;
  animated_image->recycle = &recycle;

  return animated_image;
}

bool gif_decode_info(Stream* stream, ImageInfo* info) {
  GifFileType* gif_file = NULL;

  // Open
  gif_file = DGifOpen(stream, &custom_read_fun, &error_code);
  if (gif_file == NULL) {
    WTF_OOM;
    return NULL;
  }

  info->width = (uint32_t) gif_file->SWidth;
  info->height = (uint32_t) gif_file->SHeight;
  info->format = IMAGE_FORMAT_GIF;
  info->opaque = false; // Can't get opaque state, set false
  info->frame_count = -1; // For gif, must decode all frame to get frame count.

  DGifCloseFile(gif_file, &error_code);
  return true;
}


code:
/*
 * Copyright 2015 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_IMAGE_GIF_H
#define IMAGE_IMAGE_GIF_H

#include <stdbool.h>

#include "gif_lib.h"
#include "animated_image.h"
#include "image_library.h"
#include "stream.h"

#define IMAGE_GIF_DECODER_DESCRIPTION ("giflib " MAKESTRING(STRINGIZE, GIFLIB_MAJOR) "." \
                                       MAKESTRING(STRINGIZE, GIFLIB_MINOR) "." \
                                       MAKESTRING(STRINGIZE, GIFLIB_RELEASE))

#define IMAGE_GIF_MAGIC_NUMBER_0 0x47
#define IMAGE_GIF_MAGIC_NUMBER_1 0x49

bool gif_init(ImageLibrary* library);

bool gif_is_magic(Stream* stream);

const char* gif_get_description();

AnimatedImage* gif_decode(Stream* stream, bool partially, bool* animated);

bool gif_decode_info(Stream* stream, ImageInfo* info);

#endif // IMAGE_IMAGE_GIF_H


code:
# Copyright 2018 Hippo Seven
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

cmake_minimum_required(VERSION 3.4.1)
project(image C)

set(IMAGE_SOURCES
    image.c
    image_plain.c
    image_bmp.c
    image_utils.c
    image_convert.c
    static_image.c
    delegate_image.c
    bitmap_container.c
    java_wrapper.c
    stream/stream.c
    stream/buffer_stream.c
    stream/java_stream.c
    stream/buffer.c
)

set(IMAGE_INCLUDES
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/..
    ${CMAKE_CURRENT_SOURCE_DIR}/javah
    ${CMAKE_CURRENT_SOURCE_DIR}/stream
)
set(IMAGE_EXPORT_INCLUDES ${IMAGE_INCLUDES} PARENT_SCOPE)

if(IMAGE_SINGLE_SHARED_LIB)
    set(IMAGE_DEFINITIONS
        -DIMAGE_SINGLE_SHARED_LIB
    )
endif()

set(IMAGE_LIBRARIES
    log
    jnigraphics
    GLESv2
)

if(IMAGE_SINGLE_SHARED_LIB)
    set(IMAGE_LIBRARIES
        ${IMAGE_LIBRARIES}
        image-jpeg-static
        image-png-static
        image-gif-static
        image-webp-static
    )
else()
    set(IMAGE_LIBRARIES
        ${IMAGE_LIBRARIES}
        dl
    )
endif()

if (${ANDROID_ABI} STREQUAL "armeabi-v7a")

  enable_language(ASM)

  set(IMAGE_SOURCES
      ${IMAGE_SOURCES}
      image_convert_arm.c
      image_convert_neon.S
  )
  set(IMAGE_INCLUDES
      ${IMAGE_INCLUDES}
      ${ANDROID_NDK}/sources/android/cpufeatures
  )
  set(IMAGE_DEFINITIONS
      ${IMAGE_DEFINITIONS}
      -DIMAGE_CONVERT_ARM
  )
  set(IMAGE_LIBRARIES
      ${IMAGE_LIBRARIES}
      cpufeatures
  )

endif ()

add_library(image SHARED ${IMAGE_SOURCES})
target_include_directories(image PRIVATE ${IMAGE_INCLUDES})
target_compile_definitions(image PRIVATE ${IMAGE_DEFINITIONS})
target_link_libraries(image PRIVATE ${IMAGE_LIBRARIES})


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_ANIMATED_IMAGE_H
#define IMAGE_ANIMATED_IMAGE_H

#include <stdbool.h>
#include <stdint.h>

#include "delegate_image.h"
#include "stream.h"

struct ANIMATED_IMAGE;
typedef struct ANIMATED_IMAGE AnimatedImage;

struct ANIMATED_IMAGE {
  uint32_t width;
  uint32_t height;
  int32_t format;
  bool opaque;
  bool completed;
  void* data;
  Stream* (*get_stream)(AnimatedImage* image);
  void (*complete)(AnimatedImage* image);
  uint32_t (*get_frame_count)(AnimatedImage* image);
  uint32_t (*get_delay)(AnimatedImage* image, uint32_t frame); // ms
  uint32_t (*get_byte_count)(AnimatedImage* image);
  void (*advance)(AnimatedImage* image, DelegateImage* dImage);
  void (*recycle)(AnimatedImage** image);
};

#endif //IMAGE_ANIMATED_IMAGE_H


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <malloc.h>
#include <android/bitmap.h>

#include "bitmap_container.h"
#include "../log.h"

typedef struct {
  JNIEnv* env;
  jclass clazz;
  jmethodID method;
  jobject bitmap;
} BitmapContainerData;

static void* create_buffer(BufferContainer* container, uint32_t width, uint32_t height, int32_t config) {
  BitmapContainerData* data = container->data;
  void *pixels = NULL;

  if (data->bitmap != NULL) {
    LOGE("Can't call create_buffer twice.");
    return NULL;
  }

  data->bitmap = (*data->env)->CallStaticObjectMethod(data->env, data->clazz, data->method, width, height, config);
  if (data->bitmap == NULL) {
    LOGE("Can't create bitmap.");
    return NULL;
  }

  AndroidBitmap_lockPixels(data->env, data->bitmap, &pixels);
  if (pixels == NULL) {
    LOGE("Can't lock pixels of Bitmap.");
  }

  return pixels;
}

static void release_buffer(BufferContainer* container, void* buffer) {
  BitmapContainerData* data = container->data;

  if (buffer == NULL) {
    LOGE("Can't release NULL buffer.");
    return;
  }

  if (data->bitmap == NULL) {
    LOGE("Can't release buffer when bitmap is NULL.");
    return;
  }

  AndroidBitmap_unlockPixels(data->env, data->bitmap);
}

BufferContainer* bitmap_container_new(JNIEnv* env, jclass clazz, jmethodID method) {
  BufferContainer* container;
  BitmapContainerData* data;

  container = malloc(sizeof(BufferContainer));
  data = malloc(sizeof(BitmapContainerData));
  if (container == NULL || data == NULL) {
    WTF_OOM;
    free(container);
    free(data);
    return NULL;
  }

  data->env = env;
  data->clazz = clazz;
  data->method = method;
  data->bitmap = NULL;

  container->data = data;
  container->create_buffer = &create_buffer;
  container->release_buffer = &release_buffer;

  return container;
}

jobject bitmap_container_fetch_bitmap(BufferContainer *container) {
  BitmapContainerData* data = container->data;
  jobject bitmap = data->bitmap;
  data->bitmap = NULL;
  return bitmap;
}

void bitmap_container_recycle(BufferContainer** container) {
  BitmapContainerData* data;

  if (container == NULL || *container == NULL) {
    return;
  }

  data = (BitmapContainerData*) (*container)->data;
  if (data->bitmap != NULL) {
    LOGE("Call bitmap_container_fetch_bitmap() before recycle()");
  }

  free(data);
  (*container)->data = NULL;
  free(*container);
  *container = NULL;
}


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_BITMAP_CONTAINER_H
#define IMAGE_BITMAP_CONTAINER_H

#include <jni.h>

#include "buffer_container.h"

BufferContainer* bitmap_container_new(JNIEnv* env, jclass clazz, jmethodID method);

jobject bitmap_container_fetch_bitmap(BufferContainer *container);

void bitmap_container_recycle(BufferContainer** container);

#endif //IMAGE_BITMAP_CONTAINER_H


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_BUFFER_CONTAINER_H
#define IMAGE_BUFFER_CONTAINER_H

#include <stdint.h>

struct BUFFER_CONTAINER;
typedef struct BUFFER_CONTAINER BufferContainer;

struct BUFFER_CONTAINER {
  void* data;
  void* (*create_buffer)(BufferContainer* container, uint32_t width, uint32_t height, int32_t config);
  void (*release_buffer)(BufferContainer* container, void* buffer);
};

#endif //IMAGE_BUFFER_CONTAINER_H


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <malloc.h>
#include <string.h>

#include "delegate_image.h"
#include "../log.h"

DelegateImage* delegate_image_new(uint32_t width, uint32_t height) {
  DelegateImage* image = malloc(sizeof(DelegateImage));
  uint8_t* buffer = malloc(width * height * 4);
  uint8_t* shown = malloc(width * height * 4);
  if (image == NULL || buffer == NULL) {
    WTF_OOM;
    free(image);
    free(buffer);
    free(shown);
    return NULL;
  }

  image->width = width;
  image->height = height;
  image->index = -1;
  image->buffer = buffer;
  image->shown = shown;
  image->backup = NULL;

  return image;
}

void delegate_image_switch_data_backup(DelegateImage* image) {
  void* temp;

  if (image->backup == NULL) {
    delegate_image_backup(image);
  } else {
    temp = image->buffer;
    image->buffer = image->backup;
    image->backup = temp;
  }
}

void delegate_image_backup(DelegateImage* image) {
  if (image->backup == NULL) {
    image->backup = malloc(image->width * image->height * 4);
    if (image->backup == NULL) {
      WTF_OOM;
      return;
    }
  }

  memcpy(image->backup, image->buffer, image->width * image->height * 4);
}

void delegate_image_restore(DelegateImage* image) {
  if (image->backup == NULL) {
    LOGE(MSG("Can't restore on null backup"));
  } else {
    memcpy(image->buffer, image->backup, image->width * image->height * 4);
  }
}

void delegate_image_apply(DelegateImage* image) {
  memcpy(image->shown, image->buffer, image->width * image->height * 4);
}

void delegate_image_delete(DelegateImage** image) {
  if (image == NULL || *image == NULL) {
    return;
  }

  free((*image)->buffer);
  (*image)->buffer = NULL;
  free((*image)->shown);
  (*image)->shown = NULL;
  free((*image)->backup);
  (*image)->backup = NULL;
  free(*image);
  *image = NULL;
}


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_DELEGATE_IMAGE_H
#define IMAGE_DELEGATE_IMAGE_H

#include <stdint.h>

typedef struct {
  uint32_t width;
  uint32_t height;
  int32_t index;
  uint8_t* buffer;
  uint8_t* shown;
  uint8_t* backup;
} DelegateImage;

DelegateImage* delegate_image_new(uint32_t width, uint32_t height);

void delegate_image_switch_data_backup(DelegateImage* image);

void delegate_image_backup(DelegateImage* image);

void delegate_image_restore(DelegateImage* image);

void delegate_image_apply(DelegateImage* image);

void delegate_image_delete(DelegateImage** image);

#endif //IMAGE_DELEGATE_IMAGE_H


code:
/*
 * Copyright 2015 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <dlfcn.h>
#include <malloc.h>

#include "image.h"
#include "../log.h"

#ifdef IMAGE_SINGLE_SHARED_LIB
  #include "image_plain.h"
  #include "image_bmp.h"
  #include "image_jpeg.h"
  #include "image_png.h"
  #include "image_gif.h"
  #include "image_webp.h"
#endif

static ImageLibrary image_libraries[IMAGE_FORMAT_MAX_COUNT] = { 0 };

#ifdef IMAGE_SINGLE_SHARED_LIB
// Load a local library and call its initializer to populate the ImageLibrary struct
static void load_library_local(const char* init_func_name, ImageLibraryInitFunc init_func, ImageLibrary* library) {
    if (!init_func || !init_func(library)) {
        LOGE(MSG("Library init func %s failed"), init_func_name);
        library->loaded = false;
        return;
    }

    LOGI(MSG("Library loaded successfully with %s()"), init_func_name);
}
#else
// Load a library from lib_name, and call its initializer to populate the ImageLibrary struct
static void load_library(const char* lib_name, const char* init_func_name, ImageLibrary* library) {
  void* handle = dlopen(lib_name, RTLD_LAZY | RTLD_LOCAL);
  if (handle == NULL) {
    LOGE(MSG("Cannot find library %s"), lib_name);
    library->loaded = false;
    return;
  }

  ImageLibraryInitFunc init_func = dlsym(handle, init_func_name);
  if (init_func == NULL) {
    LOGE(MSG("Cannot find library %s's init func %s"), lib_name, init_func_name);
    library->loaded = false;
    dlclose(handle);
    return;
  }

  if (!init_func(library)) {
    LOGE(MSG("Library %s's init func %s failed"), lib_name, init_func_name);
    library->loaded = false;
    dlclose(handle);
    return;
  }

  LOGI(MSG("Library %s loaded successfully with %s()"), lib_name, init_func_name);
}
#endif

// Dynamically load any available decoders from their shared libraries
void init_image_libraries() {
#ifdef IMAGE_SINGLE_SHARED_LIB
  load_library_local("plain_init", plain_init, &image_libraries[IMAGE_FORMAT_PLAIN]);
  load_library_local("bmp_init",   NULL,       &image_libraries[IMAGE_FORMAT_BMP]);
  load_library_local("jpeg_init",  jpeg_init,  &image_libraries[IMAGE_FORMAT_JPEG]);
  load_library_local("png_init",   png_init,   &image_libraries[IMAGE_FORMAT_PNG]);
  load_library_local("gif_init",   gif_init,   &image_libraries[IMAGE_FORMAT_GIF]);
  load_library_local("webp_init",  webp_init,  &image_libraries[IMAGE_FORMAT_WEBP]);
#else
  load_library("libimage.so",      "plain_init", &image_libraries[IMAGE_FORMAT_PLAIN]);
  load_library("libimage.so",      "bmp_init",   &image_libraries[IMAGE_FORMAT_BMP]);
  load_library("libimage-jpeg.so", "jpeg_init",  &image_libraries[IMAGE_FORMAT_JPEG]);
  load_library("libimage-png.so",  "png_init",   &image_libraries[IMAGE_FORMAT_PNG]);
  load_library("libimage-gif.so",  "gif_init",   &image_libraries[IMAGE_FORMAT_GIF]);
  load_library("libimage-webp.so", "webp_init",  &image_libraries[IMAGE_FORMAT_WEBP]);
#endif
}

static ImageLibrary* get_library_for_format(int8_t format) {
  if (format < 0 || format >= IMAGE_FORMAT_MAX_COUNT) {
    return NULL;
  }

  if (!image_libraries[format].loaded) {
    return NULL;
  }

  return &image_libraries[format];
}

static ImageLibrary* get_library_for_image(Stream* stream) {
  for (size_t i = 0; i < IMAGE_FORMAT_MAX_COUNT; i++) {
    ImageLibrary* library = &image_libraries[i];
    if (!library->loaded || library->is_magic == NULL) {
      continue;
    }

    if (library->is_magic(stream)) {
      return library;
    }
  }

  // Read several bytes for error message
  uint8_t magic[2];
  size_t read = stream->peek(stream, magic, sizeof(magic));
  if (read != sizeof(magic)) {
    LOGE(MSG("Can't read %zu magic numbers from stream"), sizeof(magic));
    return NULL;
  }

  LOGE(MSG("Can't recognize the stream with starting bytes: 0x%02x 0x%02x"), magic[0], magic[1]);
  return NULL;
}

void decode(Stream* stream, bool partially, bool* animated, void** image) {
  ImageLibrary* library = get_library_for_image(stream);
  if (library == NULL || library->decode == NULL) {
    LOGE(MSG("No valid image decode could be found"));
    *image = NULL;
    return;
  }

  *image = library->decode(stream, partially, animated);
}

bool decode_info(Stream* stream, ImageInfo* info) {
  ImageLibrary* library = get_library_for_image(stream);
  if (library == NULL || library->decode_info == NULL) {
    LOGE(MSG("No valid image decode_info could be found"));
    return false;
  }

  return library->decode_info(stream, info);
}

bool decode_buffer(Stream* stream, bool clip, uint32_t x, uint32_t y, uint32_t width,
    uint32_t height, int32_t config, uint32_t ratio, BufferContainer* container) {
  ImageLibrary* library = get_library_for_image(stream);
  if (library == NULL || library->decode_buffer == NULL) {
    LOGE(MSG("No valid image decode_buffer could be found"));
    return false;
  }

  return library->decode_buffer(stream, clip, x, y, width, height, config, ratio, container);
}

StaticImage* create(uint32_t width, uint32_t height, const uint8_t* data) {
  ImageLibrary* library = get_library_for_format(IMAGE_FORMAT_PLAIN);
  if (library == NULL || library->create == NULL) {
    LOGE(MSG("No valid image create could be found"));
    return NULL;
  }

  return library->create(width, height, data);
}

int get_supported_formats(int *formats) {
  int n = 0;
  for (size_t i = 0; i < IMAGE_FORMAT_MAX_COUNT; i++) {
    if (image_libraries[i].loaded) {
      formats[n++] = i;
    }
  }
  return n;
}

const char *get_library_description(int format) {
  ImageLibrary* library = get_library_for_format(format);
  if (library == NULL || library->get_description == NULL) {
    LOGE(MSG("No valid image get_library_description could be found"));
    return NULL;
  }

  return library->get_description();
}


code:
/*
 * Copyright 2015 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_IMAGE_H
#define IMAGE_IMAGE_H

#include <stdbool.h>

#include "com_hippo_image_Image.h"
#include "static_image.h"
#include "image_info.h"
#include "buffer_container.h"
#include "stream.h"
#include "image_library.h"

#define IMAGE_FORMAT_UNKNOWN com_hippo_image_Image_FORMAT_UNKNOWN

#define IMAGE_FORMAT_PLAIN com_hippo_image_Image_FORMAT_PLAIN

#define IMAGE_FORMAT_BMP com_hippo_image_Image_FORMAT_BMP

#define IMAGE_FORMAT_JPEG com_hippo_image_Image_FORMAT_JPEG

#define IMAGE_FORMAT_PNG com_hippo_image_Image_FORMAT_PNG

#define IMAGE_FORMAT_GIF com_hippo_image_Image_FORMAT_GIF

#define IMAGE_FORMAT_WEBP com_hippo_image_Image_FORMAT_WEBP

#define IMAGE_FORMAT_MAX_COUNT 6

void init_image_libraries();

void decode(Stream* stream, bool partially, bool* animated, void** image);

bool decode_info(Stream* stream, ImageInfo* info);

bool decode_buffer(Stream* stream, bool clip, uint32_t x, uint32_t y, uint32_t width,
    uint32_t height, int32_t config, uint32_t ratio, BufferContainer* container);

StaticImage* create(uint32_t width, uint32_t height, const uint8_t* data);

int get_supported_formats(int *formats);

const char *get_library_description(int format);

#endif //IMAGE_IMAGE_H


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "image_bmp.h"

// TODO


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_IMAGE_BMP_H
#define IMAGE_IMAGE_BMP_H

#include "static_image.h"
#include "stream.h"

#define IMAGE_BMP_DECODER_DESCRIPTION "image_bmp 0.1.0"

#define IMAGE_BMP_MAGIC_NUMBER_0 0x42 // 'B'
#define IMAGE_BMP_MAGIC_NUMBER_1 0x4D // 'M'

StaticImage* bmp_decode(Stream* stream);

#endif //IMAGE_IMAGE_BMP_H


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "image_convert.h"
#include "image_decoder.h"
#include "image_utils.h"
#include "../log.h"

#if IMAGE_CONVERT_ARM
#  include "image_convert_arm.h"
#  define IMAGE_CONVERT_SIMD_CHECK is_support_neon
#  define IMAGE_CONVERT_SIMD_RGBA8888_TO_RGBA8888_ROW_INTERNAL_2 RGBA8888_to_RGBA8888_row_internal_2_neon
#  define IMAGE_CONVERT_SIMD_RGBA8888_TO_RGB565_ROW_INTERNAL_1   RGBA8888_to_RGB565_row_internal_1_neon
#  define IMAGE_CONVERT_SIMD_RGB565_TO_RGB565_ROW_INTERNAL_2     RGB565_to_RGB565_row_internal_2_neon
#endif

#define RGB565_BLUE(c) ((c) & 0x1f)
#define RGB565_GREEN(c1, c2) ((((c1) & 0xe0) >> 5) | (((c2) & 0x7) << 3))
#define RGB565_REG(c) ((c) >> 3)

void RGBA8888_to_RGBA8888_row_internal_2(
    uint8_t* dst, const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio) {
  uint32_t i;
  uint32_t start = (ratio - 2) / 2 * 4;
  uint32_t interval = ratio * 4;

  src1 += start;
  src2 += start;
  for (i = 0; i < d_width; i++) {
    register uint16_t r, g, b, a;
    r = src1[0] + src2[0];
    g = src1[1] + src2[1];
    b = src1[2] + src2[2];
    a = src1[3] + src2[3];
    r += src1[4] + src2[4];
    g += src1[5] + src2[5];
    b += src1[6] + src2[6];
    a += src1[7] + src2[7];

    dst[0] = (uint8_t) (r / 4);
    dst[1] = (uint8_t) (g / 4);
    dst[2] = (uint8_t) (b / 4);
    dst[3] = (uint8_t) (a / 4);

    src1 += interval;
    src2 += interval;
    dst += 4;
  }
}

void RGBA8888_to_RGBA8888_row(uint8_t* dst,
    const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio) {
  if (ratio == 1) {
    memcpy(dst, src1, d_width * 4);
  } else {
#ifdef IMAGE_CONVERT_SIMD_RGBA8888_TO_RGBA8888_ROW_INTERNAL_2
#  ifdef IMAGE_CONVERT_SIMD_CHECK
    if (IMAGE_CONVERT_SIMD_CHECK()) {
#  endif
      IMAGE_CONVERT_SIMD_RGBA8888_TO_RGBA8888_ROW_INTERNAL_2(dst, src1, src2, d_width, ratio);
#  ifdef IMAGE_CONVERT_SIMD_CHECK
    } else {
      RGBA8888_to_RGBA8888_row_internal_2(dst, src1, src2, d_width, ratio);
    }
#  endif
#else
    RGBA8888_to_RGBA8888_row_internal_2(dst, src1, src2, d_width, ratio);
#endif
  }
}

static void RGBA8888_to_RGB565_row_internal_1(
    uint8_t* dst, const uint8_t* src, uint32_t width) {
  uint32_t i;
  for (i = 0; i < width; i++) {
    register uint8_t r, g, b;
    r = src[0] >> 3;
    g = src[1] >> 2;
    b = src[2] >> 3;

    dst[0] = (uint8_t) (g << 5 | b);
    dst[1] = (uint8_t) (r << 3 | g >> 3);

    src += 4;
    dst += 2;
  }
}

static void RGBA8888_to_RGB565_row_internal_2(
    uint8_t* dst, const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio) {
  uint32_t i;
  uint32_t start = (ratio - 2) / 2 * 4;
  uint32_t interval = ratio * 4;

  src1 += start;
  src2 += start;
  for (i = 0; i < d_width; i++) {
    register uint16_t r, g, b;
    r = src1[0] + src2[0];
    g = src1[1] + src2[1];
    b = src1[2] + src2[2];
    r += src1[4] + src2[4];
    g += src1[5] + src2[5];
    b += src1[6] + src2[6];
    r = (uint16_t) ((r / 4) >> 3);
    g = (uint16_t) ((g / 4) >> 2);
    b = (uint16_t) ((b / 4) >> 3);

    dst[0] = (uint8_t) (g << 5 | b);
    dst[1] = (uint8_t) (r << 3 | g >> 3);

    src1 += interval;
    src2 += interval;
    dst += 2;
  }
}

void RGBA8888_to_RGB565_row(uint8_t* dst,
    const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio) {
  if (ratio == 1) {
#ifdef IMAGE_CONVERT_SIMD_RGBA8888_TO_RGB565_ROW_INTERNAL_1
#  ifdef IMAGE_CONVERT_SIMD_CHECK
    if (IMAGE_CONVERT_SIMD_CHECK()) {
#  endif
      IMAGE_CONVERT_SIMD_RGBA8888_TO_RGB565_ROW_INTERNAL_1(dst, src1, d_width);
#  ifdef IMAGE_CONVERT_SIMD_CHECK
    } else {
      RGBA8888_to_RGB565_row_internal_1(dst, src1, d_width);
    }
#  endif
#else
    RGBA8888_to_RGB565_row_internal_1(dst, src1, d_width);
#endif
  } else {
    RGBA8888_to_RGB565_row_internal_2(dst, src1, src2, d_width, ratio);
  }
}

static void RGB565_to_RGB565_row_internal_2(
    uint8_t* dst, const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio) {
  uint32_t i;
  uint32_t start = (ratio - 2) / 2 * 2;
  uint32_t interval = ratio * 2;

  src1 += start;
  src2 += start;
  for (i = 0; i < d_width; i++) {
    register uint8_t r, g, b;

    b = (uint8_t) RGB565_BLUE(src1[0]) + (uint8_t) RGB565_BLUE(src2[0]);
    g = (uint8_t) RGB565_GREEN(src1[0], src1[1]) + (uint8_t) RGB565_GREEN(src2[0], src2[1]);
    r = RGB565_REG(src1[1]) + RGB565_REG(src2[1]);
    b += (uint8_t) RGB565_BLUE(src1[2]) + (uint8_t) RGB565_BLUE(src2[2]);
    g += (uint8_t) RGB565_GREEN(src1[2], src1[3]) + (uint8_t) RGB565_GREEN(src2[2], src2[3]);
    r += RGB565_REG(src1[3]) + RGB565_REG(src2[3]);

    b /= 4;
    g /= 4;
    r /= 4;

    dst[0] = b | g << 5;
    dst[1] = g >> 3 | r << 3;

    src1 += interval;
    src2 += interval;
    dst += 2;
  }
}

void RGB565_to_RGB565_row(uint8_t* dst,
    const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio) {
  if (ratio == 1) {
    memcpy(dst, src1, d_width * 2);
  } else {
#ifdef IMAGE_CONVERT_SIMD_RGB565_TO_RGB565_ROW_INTERNAL_2
#  ifdef IMAGE_CONVERT_SIMD_CHECK
    if (IMAGE_CONVERT_SIMD_CHECK()) {
#  endif
      IMAGE_CONVERT_SIMD_RGB565_TO_RGB565_ROW_INTERNAL_2(dst, src1, src2, d_width, ratio);
#  ifdef IMAGE_CONVERT_SIMD_CHECK
    } else {
      RGB565_to_RGB565_row_internal_2(dst, src1, src2, d_width, ratio);
    }
#  endif
#else
    RGB565_to_RGB565_row_internal_2(dst, src1, src2, d_width, ratio);
#endif
  }
}

static void memset_color(uint8_t* dst, uint8_t* color, size_t depth, size_t size) {
  for (size_t i = 0; i < size; ++i) {
    for (size_t j = 0; j < depth; ++j) {
      dst[j] = color[j];
    }
    dst += depth;
  }
}

// Use int32_t instead of uint32_t to avoid
// the type hide negative number.
static bool convert_internal(
    uint8_t* dst, int32_t dst_config,
    int32_t dst_w, int32_t dst_h,
    int32_t dst_x, int32_t dst_y,
    uint8_t* src, int32_t src_config,
    int32_t src_w, int32_t src_h,
    int32_t src_x, int32_t src_y,
    int32_t width, int32_t height,
    int32_t ratio, bool fill_blank, uint8_t* fill_color) {

  int32_t temp;
  uint32_t len;
  uint8_t* line1;
  uint8_t* line2;
  RowFunc row_func = NULL;

  // Make width and height is multiple of ratio
  width = floor_uint32_t((uint32_t) width, (uint32_t) ratio);
  height = floor_uint32_t((uint32_t) height, (uint32_t) ratio);

  // Avoid ratio is too big to render
  if (ratio > width || ratio > height) { return false; }

  // Make sure x >= 0
  if (src_x < 0) {
    temp = ceil_uint32_t((uint32_t) -src_x, (uint32_t) ratio);
    src_x += temp;
    dst_x += temp / ratio;
    width -= temp;
  }
  if (dst_x < 0) {
    temp = -dst_x * ratio;
    src_x += temp;
    dst_x = 0;
    width -= temp;
  }
  if (width <= 0) { return false; }

  // Make sure y >= 0
  if (src_y < 0) {
    temp = ceil_uint32_t((uint32_t) -src_y, (uint32_t) ratio);
    src_y += temp;
    dst_y += temp / ratio;
    height -= temp;
  }
  if (dst_y < 0) {
    temp = -dst_y * ratio;
    src_y += temp;
    dst_y = 0;
    height -= temp;
  }
  if (height <= 0) { return false; }

  // Make sure x + width <= w
  temp = src_x + width - src_w;
  if (temp > 0) {
    width -= ceil_uint32_t((uint32_t) temp, (uint32_t) ratio);
  }
  temp = dst_x + width / ratio - dst_w;
  if (temp > 0) {
    width -= temp * ratio;
  }
  if (width <= 0) { return false; }

  // Make sure y + height <= h
  temp = src_y + height - src_h;
  if (temp > 0) {
    height -= ceil_uint32_t((uint32_t) temp, (uint32_t) ratio);
  }
  temp = dst_y + height / ratio - dst_h;
  if (temp > 0) {
    height -= temp * ratio;
  }
  if (height <= 0) { return false; }

  // Assign depth
  const uint32_t src_depth = get_depth_for_config(src_config);
  const uint32_t dst_depth = get_depth_for_config(dst_config);

  // Row function
  if (src_config == IMAGE_CONFIG_RGBA_8888) {
    if (dst_config == IMAGE_CONFIG_RGBA_8888) {
      row_func = &RGBA8888_to_RGBA8888_row;
    } else if (dst_config == IMAGE_CONFIG_RGB_565) {
      row_func = &RGBA8888_to_RGB565_row;
    } else {
      LOGE("Invalid dst config: %d", dst_config);
      return false;
    }
  } else if (src_config == IMAGE_CONFIG_RGB_565) {
    if (dst_config == IMAGE_CONFIG_RGB_565) {
      row_func = &RGB565_to_RGB565_row;
    } else {
      LOGE("Invalid dst config: %d", dst_config);
      return false;
    }
  } else {
    LOGE("Invalid src config: %d", dst_config);
  }

  // Fill start blank lines
  len = (uint32_t) (dst_y * dst_w);
  if (fill_blank && len > 0) {
    memset_color(dst, fill_color, dst_depth, len);
  }
  dst += len * dst_depth;

  // Copy lines
  const uint32_t w = (uint32_t) (width / ratio);
  const uint32_t h = (uint32_t) (height / ratio);
  const uint32_t skip = (const uint32_t) ((ratio - 2) / 2);
  src += src_y * src_w * src_depth;
  for (uint32_t i = 0; i < h; ++i) {
    // Fill line start blank
    len = (uint32_t) dst_x;
    if (fill_blank && len > 0) {
      memset_color(dst, fill_color, dst_depth, len);
    }
    dst += len * dst_depth;

    // Convert
    line1 = src + ((skip * src_w + src_x) * src_depth);
    line2 = line1 + (src_w * src_depth);
    row_func(dst, line1, line2, w, (uint32_t) ratio);
    dst += w * dst_depth;

    // Fill line end blank
    len = dst_w - dst_x - w;
    if (fill_blank && len > 0) {
      memset_color(dst, fill_color, dst_depth, len);
    }
    dst += len * dst_depth;

    src += ratio * src_w * src_depth;
  }

  // Fill end blank lines
  len = (dst_h - dst_y - h) * dst_w;
  if (fill_blank && len > 0) {
    memset_color(dst, fill_color, dst_depth, len);
  }

  return true;
}

void convert(uint8_t* dst, int32_t dst_config,
    uint32_t dst_w, uint32_t dst_h,
    int32_t dst_x, int32_t dst_y,
    uint8_t* src, int32_t src_config,
    uint32_t src_w, uint32_t src_h,
    int32_t src_x, int32_t src_y,
    uint32_t width, uint32_t height,
    uint32_t ratio, bool fill_blank, uint32_t fill_color) {
  // Can't convert for not explicit config
  if (!is_explicit_config(src_config) || !is_explicit_config(dst_config)) {
    return;
  }

  // 4 is enough
  uint8_t color[4];
  size_t color_depth = get_depth_for_config(dst_config);

  if (dst_config == IMAGE_CONFIG_RGBA_8888) {
    memcpy(color, &fill_color, 4);
  } else if (dst_config == IMAGE_CONFIG_RGB_565) {
    uint8_t* p = (uint8_t *) &fill_color;
    color[0] = (uint8_t) ((p[1] >> 2) << 5 | (p[2] >> 3));
    color[1] = (uint8_t) ((p[0] >> 3) << 3 | (p[1] >> 2) >> 3);
  }

  if (!convert_internal(dst, dst_config, dst_w, dst_h, dst_x, dst_y,
      src, src_config, src_w, src_h, src_x, src_y, width, height,
      ratio, fill_blank, color) && fill_blank) {
    memset_color(dst, color, color_depth, (size_t) (dst_w * dst_h));
  }
}


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_IMAGE_CONVERT_H
#define IMAGE_IMAGE_CONVERT_H

#include <stdint.h>
#include <string.h>
#include <stdbool.h>

typedef void (*RowFunc)(uint8_t* dst,
    const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio);

void RGBA8888_to_RGBA8888_row(uint8_t* dst,
    const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio);

void RGBA8888_to_RGB565_row(uint8_t* dst,
    const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio);

void RGB565_to_RGB565_row(uint8_t* dst,
    const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio);

void convert(uint8_t* dst, int32_t dst_config,
    uint32_t dst_w, uint32_t dst_h,
    int32_t dst_x, int32_t dst_y,
    uint8_t* src, int32_t src_config,
    uint32_t src_w, uint32_t src_h,
    int32_t src_x, int32_t src_y,
    uint32_t width, uint32_t height,
    uint32_t ratio, bool fill_blank, uint32_t fill_color);

#endif //IMAGE_IMAGE_CONVERT_H


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "cpu-features.h"
#include "image_convert_arm.h"
#include "../log.h"

#define RGB565_BLUE(c) ((c) & 0x1f)
#define RGB565_GREEN(c1, c2) ((((c1) & 0xe0) >> 5) | (((c2) & 0x7) << 3))
#define RGB565_REG(c) ((c) >> 3)

static volatile unsigned int support_neon = ~0U;

bool is_support_neon() {
  if (support_neon == ~0U) {
    support_neon = (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM &&
        (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0) ? 1 : 0;
  }
  return support_neon == 1;
}

static inline void RGBA8888_align_pixels(uint8_t* src1, uint8_t* src2, uint32_t d_width, uint32_t ratio) {
  const uint32_t start = (ratio - 2) / 2;
  const uint32_t interval = ratio;
  uint32_t* s1 = (uint32_t *) src1;
  uint32_t* s2 = (uint32_t *) src2;
  uint32_t* d1 = s1;
  uint32_t* d2 = s2;
  uint32_t i;

  s1 += start;
  s2 += start;
  for (i = 0; i < d_width; i++) {
    d1[0] = s1[0];
    d1[1] = s1[1];
    d2[0] = s2[0];
    d2[1] = s2[1];

    s1 += interval;
    s2 += interval;
    d1 += 2;
    d2 += 2;
  }
}

static inline void RGB565_align_pixels(uint8_t* src1, uint8_t* src2, uint32_t d_width, uint32_t ratio) {
  const uint32_t start = (ratio - 2) / 2;
  const uint32_t interval = ratio;
  uint16_t* s1 = (uint16_t *) src1;
  uint16_t* s2 = (uint16_t *) src2;
  uint16_t* d1 = s1;
  uint16_t* d2 = s2;
  uint32_t i;

  s1 += start;
  s2 += start;
  for (i = 0; i < d_width; i++) {
    d1[0] = s1[0];
    d1[1] = s1[1];
    d2[0] = s2[0];
    d2[1] = s2[1];

    s1 += interval;
    s2 += interval;
    d1 += 2;
    d2 += 2;
  }
}

void RGBA8888_to_RGBA8888_row_internal_2_neon(
    uint8_t* dst, const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio) {
  uint32_t i;

  // Align
  if (ratio != 2) {
    RGBA8888_align_pixels((uint8_t *) src1, (uint8_t *) src2, d_width, ratio);
  }

  // align_width is multiple of 8
  uint32_t align_width = (d_width >> 3) << 3;
  rgba8888_to_rgba8888_neon_2(dst, src1, src2, d_width);
  src1 += align_width * 2 * 4;
  src2 += align_width * 2 * 4;
  dst  += align_width * 4;

  for (i = align_width; i < d_width; i++) {
    uint16_t r, g, b, a;
    r = src1[0] + src2[0];
    g = src1[1] + src2[1];
    b = src1[2] + src2[2];
    a = src1[3] + src2[3];
    r += src1[4] + src2[4];
    g += src1[5] + src2[5];
    b += src1[6] + src2[6];
    a += src1[7] + src2[7];

    dst[0] = (uint8_t) (r / 4);
    dst[1] = (uint8_t) (g / 4);
    dst[2] = (uint8_t) (b / 4);
    dst[3] = (uint8_t) (a / 4);

    src1 += 2 * 4;
    src2 += 2 * 4;
    dst += 4;
  }
}

void RGBA8888_to_RGB565_row_internal_1_neon(
    uint8_t* dst, const uint8_t* src, uint32_t width) {
  uint32_t i;

  // align_width is multiple of 8
  uint32_t align_width = (width >> 3) << 3;
  rgba8888_to_rgb565_neon(dst, src, align_width);
  src += align_width * 4;
  dst += align_width * 2;

  for (i = align_width; i < width; i++) {
    uint8_t r, g, b;
    r = src[0] >> 3;
    g = src[1] >> 2;
    b = src[2] >> 3;
    dst[0] = (uint8_t) (g << 5 | b);
    dst[1] = (uint8_t) (r << 3 | g >> 3);
    src += 4;
    dst += 2;
  }
}

void RGB565_to_RGB565_row_internal_2_neon(
    uint8_t* dst, const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio) {
  uint32_t i;

  // Align
  if (ratio != 2) {
    RGB565_align_pixels((uint8_t *) src1, (uint8_t *) src2, d_width, ratio);
  }

  // align_width is multiple of 8
  uint32_t align_width = (d_width >> 3) << 3;
  rgb565_to_rgb565_neon_2(dst, (uint8_t *) src1, (uint8_t *) src2, d_width);
  src1 += align_width * 2 * 2;
  src2 += align_width * 2 * 2;
  dst  += align_width * 2;

  for (i = align_width; i < d_width; i++) {
    register uint8_t r, g, b;

    b = (uint8_t) RGB565_BLUE(src1[0]) + (uint8_t) RGB565_BLUE(src2[0]);
    g = (uint8_t) RGB565_GREEN(src1[0], src1[1]) + (uint8_t) RGB565_GREEN(src2[0], src2[1]);
    r = RGB565_REG(src1[1]) + RGB565_REG(src2[1]);
    b += (uint8_t) RGB565_BLUE(src1[2]) + (uint8_t) RGB565_BLUE(src2[2]);
    g += (uint8_t) RGB565_GREEN(src1[2], src1[3]) + (uint8_t) RGB565_GREEN(src2[2], src2[3]);
    r += RGB565_REG(src1[3]) + RGB565_REG(src2[3]);

    b /= 4;
    g /= 4;
    r /= 4;

    dst[0] = b | g << 5;
    dst[1] = g >> 3 | r << 3;

    src1 += 2 * 2;
    src2 += 2 * 2;
    dst += 2;
  }
}


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_IMAGE_CONVERT_NEON_H
#define IMAGE_IMAGE_CONVERT_NEON_H

#include <stdlib.h>
#include <stdbool.h>

bool is_support_neon();

// count should be a multiple of 8
void rgba8888_to_rgba8888_neon_2(uint8_t* dst,
    const uint8_t* src1, const uint8_t* src2, uint32_t count);

// count should be a multiple of 8
void rgba8888_to_rgb565_neon(uint8_t* dst,
    const uint8_t* src, uint32_t count);

// count should be a multiple of 8
void rgb565_to_rgb565_neon_2(uint8_t* dst,
    const uint8_t* src1, const uint8_t* src2, uint32_t count);

void RGBA8888_to_RGBA8888_row_internal_2_neon(
    uint8_t* dst, const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio);

void RGBA8888_to_RGB565_row_internal_1_neon(
    uint8_t* dst, const uint8_t* src, uint32_t width);

void RGB565_to_RGB565_row_internal_2_neon(
    uint8_t* dst, const uint8_t* src1, const uint8_t* src2,
    uint32_t d_width, uint32_t ratio);

#endif //IMAGE_IMAGE_CONVERT_NEON_H


code:
.fpu  neon

.macro func name

.macro endfunc
        .size   \name, .-\name
        .purgem endfunc
.endm

        .text
        .align  2
        .global \name
        .type   \name, %function
.endm

/**
* Implement vpadd.u8
* x1: dst
* x2: src1
* x3: src2
* x4: dn, n is even number
* x5: d(n+1)
* x6: q(n/2)
**/
.macro vpadd_u8 x1, x2, x3, x4, x5, x6
        vpaddl.u8   \x4,    \x2
        vshr.u16    \x4,    \x4,    #1
        vpaddl.u8   \x5,    \x3
        vshr.u16    \x5,    \x5,    #1
        vmovn.i16   \x1,    \x6
.endm

/**
* Simple version vpadd_u8
* temp1, temp2, temp3
**/
.macro vpadd_u8_s x1, x2
        vpadd_u8 \x1, \x1, \x2, temp1, temp2, temp3
.endm

/**
* x1: red
* x2: green
* x3: blue
* x4: src
**/
.macro read_rgb555 x1, x2, x3, x4
        vld2.u8     {temp1, temp2}, [\x4]!  // Load data from src1
        vand.i8     \x3,    temp1,  temp4   // blue = temp1 & b00011111
        vand.i8     \x2,    temp2,  temp5   // green = temp2 & b00000111
        vshl.u8     \x2,    \x2,    #3      // green = green << 3
        vsri.u8     \x2,    temp1,  #5      // green = (green & b11111000) | (temp1 >> 5)
        vshr.u8     \x1,    temp2,  #3      // red1  = temp2 >> 3
.endm

// void rgba8888_to_rgba8888_neon_2(uint8_t* dst, uint8_t* src1, uint8_t* src2, uint32_t count);
func    rgba8888_to_rgba8888_neon_2
dst     .req    r0
src1    .req    r1
src2    .req    r2
count   .req    r3
red1    .req    d0
green1  .req    d1
blue1   .req    d2
alpha1  .req    d3
red2    .req    d4
green2  .req    d5
blue2   .req    d6
alpha2  .req    d7
red3    .req    d8
green3  .req    d9
blue3   .req    d10
alpha3  .req    d11
temp1   .req    d12
temp2   .req    d13
temp3   .req    q6
rgba8888_to_rgba8888_neon_2:
        lsr         count,  #3              // count >>= 3 // count /= 8
        teq         count,  #0              //
        beq         .loop_end2              // if (count == 0) goto loop_end2

.loop_start2:
        vld4.u8     {red1, green1, blue1, alpha1}, [src1]!    // Load r, g, b, a from src
        vld4.u8     {red2, green2, blue2, alpha2}, [src1]!    // Load r, g, b, a from src
        vpadd_u8_s  red1,   red2
        vpadd_u8_s  green1, green2
        vpadd_u8_s  blue1,  blue2
        vpadd_u8_s  alpha1, alpha2

        vld4.u8     {red2, green2, blue2, alpha2}, [src2]!    // Load r, g, b, a from src
        vld4.u8     {red3, green3, blue3, alpha3}, [src2]!    // Load r, g, b, a from src
        vpadd_u8_s  red2,   red3
        vpadd_u8_s  green2, green3
        vpadd_u8_s  blue2,  blue3
        vpadd_u8_s  alpha2, alpha3

        vhadd.u8    red1,   red1,   red2
        vhadd.u8    green1, green1, green2
        vhadd.u8    blue1,  blue1,  blue2
        vhadd.u8    alpha1, alpha1, alpha2

        vst4.8      {red1, green1, blue1, alpha1}, [dst]!

        subs        count, count,  #1      // count -= 1
        bne         .loop_start2           // if (count != 0) goto loop_start2
.loop_end2:

        bx          lr
.unreq  dst
.unreq  src1
.unreq  src2
.unreq  count
.unreq  red1
.unreq  green1
.unreq  blue1
.unreq  alpha1
.unreq  red2
.unreq  green2
.unreq  blue2
.unreq  alpha2
.unreq  red3
.unreq  green3
.unreq  blue3
.unreq  alpha3
.unreq  temp1
.unreq  temp2
.unreq  temp3
endfunc

// void rgba8888_to_rgb565_neon(uint8_t* dst, const uint8_t* src, uint32_t count);
func    rgba8888_to_rgb565_neon
dst     .req    r0
src     .req    r1
count   .req    r2
red     .req    d0
green   .req    d1
blue    .req    d2
alpha   .req    d3
rgba8888_to_rgb565_neon:
        lsr         count,  #3              // count >>= 3 // count /= 8
        teq         count,  #0              //
        beq         .loop_end1              // if (count == 0) goto loop_end1

.loop_start1:
        vld4.u8     {red, green, blue, alpha}, [src]!    // Load r, g, b, a from src

        vmov.u8     alpha, red              //
        vsri.u8     alpha, green,  #5       // a = (r & b11111000) | (g >> 5)
        vshl.u8     green, green,  #3       //
        vsri.u8     green, blue,   #3       // g = (g << 3) | (b >> 3)

        vst2.8      {green, alpha}, [dst]!

        subs        count, count,  #1      // count -= 1
        bne         .loop_start1           // if (count != 0) goto loop_start1
.loop_end1:

        bx          lr
.unreq  dst
.unreq  src
.unreq  count
.unreq  red
.unreq  green
.unreq  blue
.unreq  alpha
endfunc

// void rgb565_to_rgb565_neon_2(uint8_t* dst, const uint8_t* src1, const uint8_t* src2, uint32_t count);
func    rgb565_to_rgb565_neon_2
dst     .req    r0
src1    .req    r1
src2    .req    r2
count   .req    r3
blue1   .req    d0
green1  .req    d1
red1    .req    d2
blue2   .req    d3
green2  .req    d4
red2    .req    d5
red3    .req    d6
green3  .req    d7
blue3   .req    d8
temp1   .req    d10
temp2   .req    d11
temp3   .req    q5
temp4   .req    d12
temp5   .req    d13
rgb565_to_rgb565_neon_2:
        push        {r4}

        lsr         count,  #3              // count >>= 3 // count /= 8
        teq         count,  #0              //
        beq         .loop_end3              // if (count == 0) goto loop_end3

        mov         r4,     #0x1F
        vdup.8      temp4,  r4
        mov         r4,     #0x07
        vdup.8      temp5,  r4

.loop_start3:
        read_rgb555 red1,   green1, blue1,  src1
        read_rgb555 red2,   green2, blue2,  src1
        vpadd_u8_s  red1,   red2
        vpadd_u8_s  green1, green2
        vpadd_u8_s  blue1,  blue2

        read_rgb555 red2,   green2, blue2,  src2
        read_rgb555 red3,   green3, blue3,  src2
        vpadd_u8_s  red2,   red3
        vpadd_u8_s  green2, green3
        vpadd_u8_s  blue2,  blue3

        vhadd.u8    red1,   red1,   red2
        vhadd.u8    green1, green1, green2
        vhadd.u8    blue1,  blue1,  blue2

        vsli.u8     blue1,  green1,  #5
        vshr.u8     green1, green1,  #3
        vsli.u8     green1, red1,    #3

        vst2.8      {blue1, green1}, [dst]!

        subs        count,  count,   #1      // count -= 1
        bne         .loop_start3           // if (count != 0) goto loop_start3
.loop_end3:

        pop         {r4}
        bx          lr
.unreq  dst
.unreq  src1
.unreq  src2
.unreq  count
.unreq  red1
.unreq  green1
.unreq  blue1
.unreq  red2
.unreq  green2
.unreq  blue2
.unreq  red3
.unreq  green3
.unreq  blue3
.unreq  temp1
.unreq  temp2
.unreq  temp3
.unreq  temp4
.unreq  temp5
endfunc


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_IMAGE_DECODER_H
#define IMAGE_IMAGE_DECODER_H

#include "com_hippo_image_BitmapDecoder.h"

#define IMAGE_CONFIG_INVALID   -1;
#define IMAGE_CONFIG_AUTO      com_hippo_image_BitmapDecoder_CONFIG_AUTO
#define IMAGE_CONFIG_RGB_565   com_hippo_image_BitmapDecoder_CONFIG_RGB_565
#define IMAGE_CONFIG_RGBA_8888 com_hippo_image_BitmapDecoder_CONFIG_RGBA_8888

static inline bool is_explicit_config(int32_t config) {
  return config == IMAGE_CONFIG_RGB_565 || config == IMAGE_CONFIG_RGBA_8888;
}

static inline uint32_t get_depth_for_config(int32_t config) {
  switch (config) {
    case IMAGE_CONFIG_RGB_565:
      return 2;
    case IMAGE_CONFIG_RGBA_8888:
      return 4;
    default:
      return 0;
  }
}

#endif //IMAGE_IMAGE_DECODER_H


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_IMAGE_INFO_H
#define IMAGE_IMAGE_INFO_H

#include <stdbool.h>
#include <stdint.h>

typedef struct {
  uint32_t width;
  uint32_t height;
  int32_t format;
  bool opaque;
  int32_t frame_count;
} ImageInfo;

#endif //IMAGE_IMAGE_INFO_H


code:
/*
 * Copyright 2018 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_LIBRARY_IMAGE_H
#define IMAGE_LIBRARY_IMAGE_H

#define LIBRARY_EXPORT __attribute__ ((visibility ("default")))

struct ImageLibrary;
typedef struct ImageLibrary ImageLibrary;

typedef bool (*ImageLibraryInitFunc)(ImageLibrary* library);
typedef bool (*ImageLibraryIsMagic)(Stream* stream);
typedef void* (*ImageLibraryDecodeFunc)(Stream* stream, bool partially, bool* animated);
typedef bool (*ImageLibraryDecodeInfoFunc)(Stream* stream, ImageInfo* info);
typedef bool (*ImageLibraryDecodeBufferFunc)(Stream* stream, bool clip, uint32_t x, uint32_t y,
    uint32_t width, uint32_t height, int32_t config, uint32_t ratio, BufferContainer* container);
typedef StaticImage* (*ImageLibraryCreateFunc)(uint32_t width, uint32_t height, const uint8_t* data);
typedef const char* (*ImageLibraryGetDescription)(void);

struct ImageLibrary {
  bool loaded;

  ImageLibraryIsMagic is_magic;
  ImageLibraryDecodeFunc decode;
  ImageLibraryDecodeInfoFunc decode_info;
  ImageLibraryDecodeBufferFunc decode_buffer;
  ImageLibraryCreateFunc create;
  ImageLibraryGetDescription get_description;
};

#endif //IMAGE_LIBRARY_IMAGE_H


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <string.h>

#include "image.h"
#include "image_plain.h"
#include "image_library.h"
#include "../log.h"

LIBRARY_EXPORT
bool plain_init(ImageLibrary* library) {
  library->loaded = true;
  library->is_magic = NULL;
  library->decode = NULL;
  library->decode_info = NULL;
  library->decode_buffer = NULL;
  library->create = plain_create;
  library->get_description = NULL;

  return true;
}

StaticImage* plain_create(uint32_t width, uint32_t height, const uint8_t* buffer) {
  StaticImage* image;

  image = static_image_new(width, height);
  if (image == NULL) { WTF_OOM; return NULL; }

  memcpy(image->buffer, buffer, width * height * 4);

  image->format = IMAGE_FORMAT_PLAIN;
  // Set opaque to false to safe
  image->opaque = false;
  return image;
}


code:
/*
 * Copyright 2016 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_IMAGE_PLAIN_H
#define IMAGE_IMAGE_PLAIN_H

#include "static_image.h"

bool plain_init(ImageLibrary* library);

StaticImage* plain_create(uint32_t width, uint32_t height, const uint8_t* buffer);

#endif // IMAGE_IMAGE_PLAIN_H


code:
/*
 * Copyright 2015 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "image_utils.h"

uint32_t floor_uint32_t(uint32_t num, uint32_t multiple) {
  uint32_t remainder = num % multiple;
  if (remainder == 0) {
    return num;
  } else {
    return num - remainder;
  }
}

uint32_t ceil_uint32_t(uint32_t num, uint32_t multiple) {
  uint32_t remainder = num % multiple;
  if (remainder == 0) {
    return num;
  } else {
    return num - remainder + multiple;
  }
}

#if __SIZEOF_SIZE_T__ == __SIZEOF_INT__
#  define __builtin_clzst __builtin_clz
#  define SIZE_T_ONE 1U
#elif __SIZEOF_SIZE_T__ == __SIZEOF_LONG__
#  define __builtin_clzst __builtin_clzl
#  define SIZE_T_ONE 1UL
#elif __SIZEOF_SIZE_T__ == __SIZEOF_LONG_LONG__
#  define __builtin_clzst __builtin_clzll
#  define SIZE_T_ONE 1ULL
#else
#  error BAD size_t
#endif

size_t next_pow2_size_t(size_t x) {
  return SIZE_T_ONE << (__SIZEOF_SIZE_T__ * 8 - __builtin_clzst(x - SIZE_T_ONE));
}


code:
/*
 * Copyright 2015 Hippo Seven
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_IMAGE_UTILS_H
#define IMAGE_IMAGE_UTILS_H

#include <stdlib.h>
#include <stdbool.h>

uint32_t floor_uint32_t(uint32_t num, uint32_t multiple);

uint32_t ceil_uint32_t(uint32_t num, uint32_t multiple);

size_t next_pow2_size_t(size_t x);

#endif //IMAGE_IMAGE_UTILS_H