/* -*- coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*- */

/*
 * Playfield Engine
 * Image HAL (common software implementation)
 */

/*-
 * SPDX-License-Identifier: Zlib
 *
 * Copyright (c) 2025-2026 Awe Morris
 * Copyright (c) 1996-2024 Keiichi Tabata
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *      claim that you wrote the original software. If you use this software
 *      in a product, an acknowledgment in the product documentation would be
 *      appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *      misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */

#include <strato/strato.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#include <assert.h>

#if defined(HAL_TARGET_WINDOWS)
#include <malloc.h>	/* _aligned_mallo() */
#endif

/* 512-bit alignment */
#define ALIGN_BYTES	(64)

/* Scanline max  */
#define SC_LINES	(1024)

/* Texture ID */
static int id_top;

/*
 * SSE Flags
 */
#if defined(HAL_USE_SSE_DISPATCH)
extern bool is_avx2_available;
extern bool is_avx_available;
extern bool is_sse42_available;
extern bool is_sse4_available;
extern bool is_sse3_available;
extern bool is_sse2_available;
extern bool is_sse_available;
#endif

/*
 * Scanline Conversion Buffer
 */
struct scbuf {
	int sc_min_x;
	int sc_max_x;
	float sc_min_tx;
	float sc_max_tx;
	float sc_min_ty;
	float sc_max_ty;
};
static struct scbuf scbuf1[SC_LINES];
static struct scbuf scbuf2[SC_LINES];

/*
 * Forward Declaraion
 */

bool
check_draw_image(
	struct hal_image *dst_image,
	int *dst_left,
	int *dst_top,
	struct hal_image *src_image,
	int *width,
	int *height,
	int *src_left,
	int *src_top,
	int alpha);

#if defined(HAL_TARGET_WINDOWS)
static void *wrap_aligned_malloc(size_t size, size_t align);
static void wrap_aligned_free(void *p);
#endif

/*
 * Initialization
 */

void init_image(void)
{
	id_top = 0;
}

void cleanup_image(void)
{
	id_top = 0;
}

/*
 * Create an image.
 */
bool
hal_create_image(
	int w,
	int h,
	struct hal_image **img)
{
	hal_pixel_t *pixels;

	assert(w > 0 && h > 0);
	assert(img != NULL);

	/* Allocate a struct buffer. */
	*img = malloc(sizeof(struct hal_image));
	if (*img == NULL) {
		hal_log_out_of_memory();
		return false;
	}
	memset(*img, 0, sizeof(struct hal_image));

	/* Allocate a pixel buffer. */
#if defined(HAL_TARGET_WINDOWS)
	pixels = wrap_aligned_malloc((size_t)w * (size_t)h * sizeof(hal_pixel_t), 64);
	if (pixels == NULL) {
		hal_log_out_of_memory();
		free(*img);
		return false;
	}
#elif defined(HAL_TARGET_SOLARIS10)
	pixels = memalign(64, (size_t)w * (size_t)h * sizeof(hal_pixel_t));
	if (pixels == NULL) {
		hal_log_out_of_memory();
		free(*img);
		return false;
	}
#elif !defined(HAL_TARGET_UNITY) && \
      !defined(HAL_TARGET_PC98) &&  \
      !defined(HAL_TARGET_PCAT) &&  \
      !defined(HAL_TARGET_MACOS7)
	if (posix_memalign((void **)&pixels, 64, (size_t)w * (size_t)h * sizeof(hal_pixel_t)) != 0) {
		hal_log_out_of_memory();
		free(*img);
		return false;
	}
#else
	pixels = malloc((size_t)w * (size_t)h * sizeof(hal_pixel_t));
	if (pixels == NULL) {
		hal_log_out_of_memory();
		free(*img);
		return false;
	}
#endif

	/* Initialize struct members. */
	(*img)->width = w;
	(*img)->height = h;
	(*img)->pixels = pixels;
	(*img)->no_free = false;
	(*img)->id = id_top++;

	return true;
}

/*
 * Create an image with a pixel buffer.
 */
bool
hal_create_image_with_pixels(
	int w,
	int h,
	hal_pixel_t *pixels,
	struct hal_image **img)
{
	assert(w > 0 && h > 0);
	assert(img != NULL);

	/* Alloc a struct buffer. */
	*img = malloc(sizeof(struct hal_image));
	if (*img == NULL) {
		hal_log_out_of_memory();
		return false;
	}
	memset(*img, 0, sizeof(struct hal_image));

	/* Initialize the members. */
	(*img)->width = w;
	(*img)->height = h;
	(*img)->pixels = pixels;
	(*img)->no_free = true;
	(*img)->id = id_top++;

	return true;
}

/*
 * Destroy an image.
 */
void
hal_destroy_image(
	struct hal_image *img)
{
	assert(img != NULL);
	assert(img->width > 0 && img->height > 0);
	assert(img->pixels != NULL);

	/* Free a texture. */
	hal_notify_image_free(img);

	/* Free a pixel buffer. */
	if (!img->no_free) {
#if defined(HAL_TARGET_WINDOWS)
		wrap_aligned_free(img->pixels);
#else
		free(img->pixels);
#endif
	}
	img->pixels = NULL;

	/* Free a struct buffer. */
	free(img);
}

/*
 * Clear
 */

/*
 * Clear an image with black color.
 */
void
hal_clear_image(
	struct hal_image *img,
	hal_pixel_t cl)
{
	hal_clear_image_rect(img, 0, 0, img->width, img->height, cl);
}

/*
 * Clear a rectangle with a specified color.
 */
void
hal_clear_image_rect(
	struct hal_image *img,
	int x,
	int y,
	int w,
	int h,
	hal_pixel_t color)
{
	hal_pixel_t *pixels;
	int i, j, sx, sy;

	assert(img != NULL);
	assert(img->width > 0 && img->height > 0);
	assert(img->pixels != NULL);

	/* Return if no need for drawing. */
	if(w == 0 || h == 0)
		return;
	sx = sy = 0;
	if(!hal_clip_by_dest(img->width, img->height, &w, &h, &x, &y, &sx, &sy))
		return;

	assert(x >= 0 && x < img->width);
	assert(w >= 0 && x + w <= img->width);
	assert(y >= 0 && y < img->height);
	assert(h >= 0 && y + h <= img->height);

	/* Clear pixels of a rectangle. */
	pixels = img->pixels;
	for (i = y; i < y + h; i++)
		for (j = x; j < x + w; j++)
			pixels[img->width * i + j] = color;

	/* Request a texture update. */
	hal_notify_image_update(img);
}

/*
 * Clear alpha channel with 255.
 */
void
hal_fill_image_alpha(
	struct hal_image *img)
{
	hal_pixel_t *p;
	int y, x;

	assert(img != NULL);

	p = img->pixels;
	for (y = 0; y < img->height; y++) {
		for (x = 0; x < img->width; x++) {
			*p = (*p) | 0xff000000;
			p++;
		}
	}
}

/*
 * Drawing
 */

#if !defined(HAL_USE_SSE_DISPATCH)

/*
 * Define the draw_image_*() functions directly.
 */
#define DRAW_IMAGE_COPY		hal_draw_image_copy
#define DRAW_IMAGE_ALPHA	hal_draw_image_alpha
#define DRAW_IMAGE_GLYPH	hal_draw_image_glyph
#define DRAW_IMAGE_EMOJI	hal_draw_image_emoji
#define DRAW_IMAGE_ADD		hal_draw_image_add
#define DRAW_IMAGE_SUB		hal_draw_image_sub
#define DRAW_IMAGE_DIM		hal_draw_image_dim
#define DRAW_IMAGE_RULE		hal_draw_image_rule
#define DRAW_IMAGE_MELT		hal_draw_image_melt
#define DRAW_IMAGE_CROSS	hal_draw_image_cross
#define DRAW_IMAGE_3D_ALPHA	hal_draw_image_3d_alpha
#define DRAW_IMAGE_3D_ADD	hal_draw_image_3d_add
#define DRAW_IMAGE_3D_SUB	hal_draw_image_3d_sub
#define DRAW_IMAGE_3D_DIM	hal_draw_image_3d_dim
#define DRAW_IMAGE_3D_CROSS	hal_draw_image_3d_cross
#include "drawimage.h"

#else

/*
 * Define the dispatch functions.
 */

void
hal_draw_image_copy(
	struct hal_image *dst_image,
	int dst_left,
	int dst_top,
	struct hal_image *src_image,
	int width,
	int height,
	int src_left,
	int src_top)
{
	void hal_draw_image_copy_avx2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_copy_avx(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_copy_sse42(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_copy_sse4(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_copy_sse3(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_copy_sse2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_copy_sse(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_copy_scalar(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_copy_avx2(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top);
	else if (is_avx_available)
		hal_draw_image_copy_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_copy_sse42(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top);
	else if (is_sse4_available)
		hal_draw_image_copy_sse4(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top);
	else if (is_sse3_available)
		hal_draw_image_copy_sse3(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top);
#endif
	else if (is_sse2_available)
		hal_draw_image_copy_sse2(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_copy_sse(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top);
#endif
	else
		hal_draw_image_copy_scalar(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top);
}

void
hal_draw_image_alpha(
	struct hal_image *dst_image,
	int dst_left,
	int dst_top,
	struct hal_image *src_image,
	int width,
	int height,
	int src_left,
	int src_top,
	int alpha)
{
	void hal_draw_image_alpha_avx2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_alpha_avx(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_alpha_sse42(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_alpha_sse4(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_alpha_sse3(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_alpha_sse2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_alpha_sse(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_alpha_scalar(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_alpha_avx2(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_avx_available)
		hal_draw_image_alpha_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_alpha_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse4_available)
		hal_draw_image_alpha_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse3_available)
		hal_draw_image_alpha_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else if (is_sse2_available)
		hal_draw_image_alpha_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_alpha_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else
		hal_draw_image_alpha_scalar(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
}

void
hal_draw_image_glyph(
	struct hal_image *dst_image,
	int dst_left,
	int dst_top,
	struct hal_image *src_image,
	int width,
	int height,
	int src_left,
	int src_top,
	int alpha)
{
	void hal_draw_image_glyph_avx2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_glyph_avx(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_glyph_sse42(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_glyph_sse4(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_glyph_sse3(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_glyph_sse2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_glyph_sse(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_glyph_scalar(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_glyph_avx2(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_avx_available)
		hal_draw_image_glyph_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_glyph_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse4_available)
		hal_draw_image_glyph_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse3_available)
		hal_draw_image_glyph_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else if (is_sse2_available)
		hal_draw_image_glyph_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_glyph_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else
		hal_draw_image_glyph_scalar(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
}

void
hal_draw_image_emoji(
	struct hal_image *dst_image,
	int dst_left,
	int dst_top,
	struct hal_image *src_image,
	int width,
	int height,
	int src_left,
	int src_top,
	int alpha)
{
	void hal_draw_image_emoji_avx2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_emoji_avx(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_emoji_sse42(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_emoji_sse4(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_emoji_sse3(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_emoji_sse2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_emoji_sse(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_emoji_scalar(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_emoji_avx2(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_avx_available)
		hal_draw_image_emoji_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_emoji_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse4_available)
		hal_draw_image_emoji_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse3_available)
		hal_draw_image_emoji_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else if (is_sse2_available)
		hal_draw_image_emoji_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_emoji_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else
		hal_draw_image_emoji_scalar(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
}

void
hal_draw_image_add(
	struct hal_image *dst_image,
	int dst_left,
	int dst_top,
	struct hal_image *src_image,
	int width,
	int height,
	int src_left,
	int src_top,
	int alpha)
{
	void hal_draw_image_add_avx2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_add_avx(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_add_sse42(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_add_sse4(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_add_sse3(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_add_sse2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_add_sse(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_add_scalar(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_add_avx2(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_avx_available)
		hal_draw_image_add_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_add_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse4_available)
		hal_draw_image_add_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse3_available)
		hal_draw_image_add_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else if (is_sse2_available)
		hal_draw_image_add_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_add_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else
		hal_draw_image_add_scalar(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
}

void
hal_draw_image_sub(
	struct hal_image *dst_image,
	int dst_left,
	int dst_top,
	struct hal_image *src_image,
	int width,
	int height,
	int src_left,
	int src_top,
	int alpha)
{
	void hal_draw_image_sub_avx2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_sub_avx(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_sub_sse42(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_sub_sse4(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_sub_sse3(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_sub_sse2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_sub_sse(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_sub_scalar(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_sub_avx2(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_avx_available)
		hal_draw_image_sub_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_sub_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse4_available)
		hal_draw_image_sub_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse3_available)
		hal_draw_image_sub_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else if (is_sse2_available)
		hal_draw_image_sub_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_sub_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else
		hal_draw_image_sub_scalar(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
}

void
hal_draw_image_dim(
	struct hal_image *dst_image,
	int dst_left,
	int dst_top,
	struct hal_image *src_image,
	int width,
	int height,
	int src_left,
	int src_top,
	int alpha)
{
	void hal_draw_image_dim_avx2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_dim_avx(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_dim_sse42(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_dim_sse4(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_dim_sse3(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_dim_sse2(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_dim_sse(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);
	void hal_draw_image_dim_scalar(struct hal_image *, int dst_left, int, struct hal_image *, int, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_dim_avx2(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_avx_available)
		hal_draw_image_dim_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_dim_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse4_available)
		hal_draw_image_dim_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
	else if (is_sse3_available)
		hal_draw_image_dim_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else if (is_sse2_available)
		hal_draw_image_dim_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_dim_avx(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
#endif
	else
		hal_draw_image_dim_scalar(dst_image, dst_left, dst_top, src_image, width, height, src_left, src_top, alpha);
}

void
hal_draw_image_rule(
	struct hal_image *dst_image,
	struct hal_image *src_image,
	struct hal_image *rule_image,
	int threshold)
{
	void hal_draw_image_rule_avx2(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_rule_avx(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_rule_sse42(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_rule_sse4(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_rule_sse3(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_rule_sse2(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_rule_sse(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_rule_scalar(struct hal_image *, struct hal_image *, struct hal_image *, int);

	if (is_avx2_available)
		hal_draw_image_rule_avx2(dst_image, src_image, rule_image, threshold);
	else if (is_avx_available)
		hal_draw_image_rule_avx(dst_image, src_image, rule_image, threshold);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_rule_sse42(dst_image, src_image, rule_image, threshold);
	else if (is_sse4_available)
		hal_draw_image_rule_sse4(dst_image, src_image, rule_image, threshold);
	else if (is_sse3_available)
		hal_draw_image_rule_sse3(dst_image, src_image, rule_image, threshold);
#endif
	else if (is_sse2_available)
		hal_draw_image_rule_sse2(dst_image, src_image, rule_image, threshold);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_rule_sse(dst_image, src_image, rule_image, threshold);
#endif
	else
		hal_draw_image_rule_scalar(dst_image, src_image, rule_image, threshold);
}

void
hal_draw_image_melt(
	struct hal_image *dst_image,
	struct hal_image *src_image,
	struct hal_image *rule_image,
	int threshold)
{
	void hal_draw_image_melt_avx2(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_melt_avx(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_melt_sse42(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_melt_sse4(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_melt_sse3(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_melt_sse2(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_melt_sse(struct hal_image *, struct hal_image *, struct hal_image *, int);
	void hal_draw_image_melt_scalar(struct hal_image *, struct hal_image *, struct hal_image *, int);

	if (is_avx2_available)
		hal_draw_image_melt_avx2(dst_image, src_image, rule_image, threshold);
	else if (is_avx_available)
		hal_draw_image_melt_avx(dst_image, src_image, rule_image, threshold);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_melt_sse42(dst_image, src_image, rule_image, threshold);
	else if (is_sse4_available)
		hal_draw_image_melt_sse4(dst_image, src_image, rule_image, threshold);
	else if (is_sse3_available)
		hal_draw_image_melt_sse3(dst_image, src_image, rule_image, threshold);
#endif
	else if (is_sse2_available)
		hal_draw_image_melt_sse2(dst_image, src_image, rule_image, threshold);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_melt_sse(dst_image, src_image, rule_image, threshold);
#endif
	else
		hal_draw_image_melt_scalar(dst_image, src_image, rule_image, threshold);
}

void
hal_draw_image_cross(
	struct hal_image *dst_image,
	struct hal_image *src1_image,
	struct hal_image *src2_image,
	int src1_left,
	int src1_top,
	int src1_width,
	int src1_height,
	int src2_left,
	int src2_top,
	int src2_width,
	int src2_height,
	int alpha)
{
	void hal_draw_image_cross_avx2(struct hal_image *, struct hal_image *, struct hal_image *, int, int, int, int, int, int, int, int, int);
	void hal_draw_image_cross_avx(struct hal_image *, struct hal_image *, struct hal_image *, int, int, int, int, int, int, int, int, int);
	void hal_draw_image_cross_sse42(struct hal_image *, struct hal_image *, struct hal_image *, int, int, int, int, int, int, int, int, int);
	void hal_draw_image_cross_sse4(struct hal_image *, struct hal_image *, struct hal_image *, int, int, int, int, int, int, int, int, int);
	void hal_draw_image_cross_sse3(struct hal_image *, struct hal_image *, struct hal_image *, int, int, int, int, int, int, int, int, int);
	void hal_draw_image_cross_sse2(struct hal_image *, struct hal_image *, struct hal_image *, int, int, int, int, int, int, int, int, int);
	void hal_draw_image_cross_sse(struct hal_image *, struct hal_image *, struct hal_image *, int, int, int, int, int, int, int, int, int);
	void hal_draw_image_cross_scalar(struct hal_image *, struct hal_image *, struct hal_image *, int, int, int, int, int, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_cross_avx2(dst_image, src1_image, src2_image, src1_left, src1_top, src1_width, src1_height, src2_left, src2_top, src2_width, src2_height, alpha);
	else if (is_avx_available)
		hal_draw_image_cross_avx(dst_image, src1_image, src2_image, src1_left, src1_top, src1_width, src1_height, src2_left, src2_top, src2_width, src2_height, alpha);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_cross_sse42(dst_image, src1_image, src2_image, src1_left, src1_top, src1_width, src1_height, src2_left, src2_top, src2_width, src2_height, alpha);
	else if (is_sse4_available)
		hal_draw_image_cross_sse4(dst_image, src1_image, src2_image, src1_left, src1_top, src1_width, src1_height, src2_left, src2_top, src2_width, src2_height, alpha);
	else if (is_sse3_available)
		hal_draw_image_cross_sse3(dst_image, src1_image, src2_image, src1_left, src1_top, src1_width, src1_height, src2_left, src2_top, src2_width, src2_height, alpha);
#endif
	else if (is_sse2_available)
		hal_draw_image_cross_sse2(dst_image, src1_image, src2_image, src1_left, src1_top, src1_width, src1_height, src2_left, src2_top, src2_width, src2_height, alpha);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_cross_sse(dst_image, src1_image, src2_image, src1_left, src1_top, src1_width, src1_height, src2_left, src2_top, src2_width, src2_height, alpha);
#endif
	else
		hal_draw_image_cross_scalar(dst_image, src1_image, src2_image, src1_left, src1_top, src1_width, src1_height, src2_left, src2_top, src2_width, src2_height, alpha);
}

void
hal_draw_image_3d_alpha(
	struct hal_image *dst_image,
	float x1,
	float y1,
	float x2,
	float y2,
	float x3,
	float y3,
	float x4,
	float y4,
	struct hal_image *src_image,
	int src_left,
	int src_top,
	int src_width,
	int src_height,
	int alpha)
{
	void hal_draw_image_3d_alpha_avx2(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_alpha_avx(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_alpha_sse42(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_alpha_sse4(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_alpha_sse3(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_alpha_sse2(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_alpha_sse(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_alpha_scalar(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_3d_alpha_avx2(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_avx_available)
		hal_draw_image_3d_alpha_avx(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_3d_alpha_sse42(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_sse4_available)
		hal_draw_image_3d_alpha_sse4(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_sse3_available)
		hal_draw_image_3d_alpha_sse3(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#endif
	else if (is_sse2_available)
		hal_draw_image_3d_alpha_sse2(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_3d_alpha_sse(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#endif
	else
		hal_draw_image_3d_alpha_scalar(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
}

void
hal_draw_image_3d_add(
	struct hal_image *dst_image,
	float x1,
	float y1,
	float x2,
	float y2,
	float x3,
	float y3,
	float x4,
	float y4,
	struct hal_image *src_image,
	int src_left,
	int src_top,
	int src_width,
	int src_height,
	int alpha)
{
	void hal_draw_image_3d_add_avx2(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_add_avx(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_add_sse42(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_add_sse4(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_add_sse3(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_add_sse2(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_add_sse(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_add_scalar(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_3d_add_avx2(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_avx_available)
		hal_draw_image_3d_add_avx(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_3d_add_sse42(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_sse4_available)
		hal_draw_image_3d_add_sse4(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_sse3_available)
		hal_draw_image_3d_add_sse3(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#endif
	else if (is_sse2_available)
		hal_draw_image_3d_add_sse2(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_3d_add_sse(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#endif
	else
		hal_draw_image_3d_add_scalar(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
}

void
hal_draw_image_3d_sub(
	struct hal_image *dst_image,
	float x1,
	float y1,
	float x2,
	float y2,
	float x3,
	float y3,
	float x4,
	float y4,
	struct hal_image *src_image,
	int src_left,
	int src_top,
	int src_width,
	int src_height,
	int alpha)
{
	void hal_draw_image_3d_sub_avx2(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_sub_avx(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_sub_sse42(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_sub_sse4(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_sub_sse3(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_sub_sse2(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_sub_sse(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_sub_scalar(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_3d_sub_avx2(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_avx_available)
		hal_draw_image_3d_sub_avx(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_3d_sub_sse42(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_sse4_available)
		hal_draw_image_3d_sub_sse4(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_sse3_available)
		hal_draw_image_3d_sub_sse3(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#endif
	else if (is_sse2_available)
		hal_draw_image_3d_sub_sse2(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_3d_sub_sse(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#endif
	else
		hal_draw_image_3d_sub_scalar(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
}

void
hal_draw_image_3d_dim(
	struct hal_image *dst_image,
	float x1,
	float y1,
	float x2,
	float y2,
	float x3,
	float y3,
	float x4,
	float y4,
	struct hal_image *src_image,
	int src_left,
	int src_top,
	int src_width,
	int src_height,
	int alpha)
{
	void hal_draw_image_3d_dim_avx2(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_dim_avx(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_dim_sse42(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_dim_sse4(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_dim_sse3(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_dim_sse2(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_dim_sse(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);
	void hal_draw_image_3d_dim_scalar(struct hal_image *, int, int, int, int, int, int, int, int, struct hal_image *, int, int, int, int);

	if (is_avx2_available)
		hal_draw_image_3d_dim_avx2(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_avx_available)
		hal_draw_image_3d_dim_avx(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_3d_dim_sse42(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_sse4_available)
		hal_draw_image_3d_dim_sse4(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
	else if (is_sse3_available)
		hal_draw_image_3d_dim_sse3(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#endif
	else if (is_sse2_available)
		hal_draw_image_3d_dim_sse2(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_3d_dim_sse(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
#endif
	else
		hal_draw_image_3d_dim_scalar(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src_image, src_left, src_top, src_width, src_height);
}

void
hal_draw_image_3d_cross(
	struct hal_image *dst_image,
	float x1,
	float y1,
	float x2,
	float y2,
	float x3,
	float y3,
	float x4,
	float y4,
	struct hal_image *src1_image,
	struct hal_image *src2_image,
	float src1_tx1,
	float src1_ty1,
	float src1_tx2,
	float src1_ty2,
	float src1_tx3,
	float src1_ty3,
	float src1_tx4,
	float src1_ty4,
	float src2_tx1,
	float src2_ty1,
	float src2_tx2,
	float src2_ty2,
	float src2_tx3,
	float src2_ty3,
	float src2_tx4,
	float src2_ty4,
	int alpha)
{
	void hal_draw_image_3d_cross_avx2(struct hal_image *, float, float, float, float, float, float, float, float, struct hal_image *, struct hal_image *, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, int);
	void hal_draw_image_3d_cross_avx(struct hal_image *, float, float, float, float, float, float, float, float, struct hal_image *, struct hal_image *, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, int);
	void hal_draw_image_3d_cross_sse42(struct hal_image *, float, float, float, float, float, float, float, float, struct hal_image *, struct hal_image *, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, int);
	void hal_draw_image_3d_cross_sse4(struct hal_image *, float, float, float, float, float, float, float, float, struct hal_image *, struct hal_image *, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, int);
	void hal_draw_image_3d_cross_sse3(struct hal_image *, float, float, float, float, float, float, float, float, struct hal_image *, struct hal_image *, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, int);
	void hal_draw_image_3d_cross_sse2(struct hal_image *, float, float, float, float, float, float, float, float, struct hal_image *, struct hal_image *, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, int);
	void hal_draw_image_3d_cross_sse(struct hal_image *, float, float, float, float, float, float, float, float, struct hal_image *, struct hal_image *, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, int);
	void hal_draw_image_3d_cross_scalar(struct hal_image *, float, float, float, float, float, float, float, float, struct hal_image *, struct hal_image *, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, int);

	if (is_avx2_available)
		hal_draw_image_3d_cross_avx2(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src1_image, src2_image, src1_tx1, src1_ty1, src1_tx2, src1_ty2, src1_tx3, src1_ty3, src1_tx4, src1_ty4, src2_tx1, src2_ty1, src2_tx2, src2_ty2, src2_tx3, src2_ty3, src2_tx4, src2_ty4, alpha);
	else if (is_avx_available)
		hal_draw_image_3d_cross_avx(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src1_image, src2_image, src1_tx1, src1_ty1, src1_tx2, src1_ty2, src1_tx3, src1_ty3, src1_tx4, src1_ty4, src2_tx1, src2_ty1, src2_tx2, src2_ty2, src2_tx3, src2_ty3, src2_tx4, src2_ty4, alpha);
#if !defined(_MSC_VER)
	else if (is_sse42_available)
		hal_draw_image_3d_cross_sse42(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src1_image, src2_image, src1_tx1, src1_ty1, src1_tx2, src1_ty2, src1_tx3, src1_ty3, src1_tx4, src1_ty4, src2_tx1, src2_ty1, src2_tx2, src2_ty2, src2_tx3, src2_ty3, src2_tx4, src2_ty4, alpha);
	else if (is_sse4_available)
		hal_draw_image_3d_cross_sse4(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src1_image, src2_image, src1_tx1, src1_ty1, src1_tx2, src1_ty2, src1_tx3, src1_ty3, src1_tx4, src1_ty4, src2_tx1, src2_ty1, src2_tx2, src2_ty2, src2_tx3, src2_ty3, src2_tx4, src2_ty4, alpha);
	else if (is_sse3_available)
		hal_draw_image_3d_cross_sse3(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src1_image, src2_image, src1_tx1, src1_ty1, src1_tx2, src1_ty2, src1_tx3, src1_ty3, src1_tx4, src1_ty4, src2_tx1, src2_ty1, src2_tx2, src2_ty2, src2_tx3, src2_ty3, src2_tx4, src2_ty4, alpha);
#endif
	else if (is_sse2_available)
		hal_draw_image_3d_cross_sse2(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src1_image, src2_image, src1_tx1, src1_ty1, src1_tx2, src1_ty2, src1_tx3, src1_ty3, src1_tx4, src1_ty4, src2_tx1, src2_ty1, src2_tx2, src2_ty2, src2_tx3, src2_ty3, src2_tx4, src2_ty4, alpha);
#if !defined(_MSC_VER) && defined(HAL_ARCH_X86)
	else if (is_sse_available)
		hal_draw_image_3d_cross_sse(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src1_image, src2_image, src1_tx1, src1_ty1, src1_tx2, src1_ty2, src1_tx3, src1_ty3, src1_tx4, src1_ty4, src2_tx1, src2_ty1, src2_tx2, src2_ty2, src2_tx3, src2_ty3, src2_tx4, src2_ty4, alpha);
#endif
	else
		hal_draw_image_3d_cross_scalar(dst_image, x1, y1, x2, y2, x3, y3, x4, y4, src1_image, src2_image, src1_tx1, src1_ty1, src1_tx2, src1_ty2, src1_tx3, src1_ty3, src1_tx4, src1_ty4, src2_tx1, src2_ty1, src2_tx2, src2_ty2, src2_tx3, src2_ty3, src2_tx4, src2_ty4, alpha);
}

#endif

/*
 * Scanline Conversion (for 3D polygon drawing)
 */

static INLINE int fp32_eq(float a, float b)
{
	float d = a - b;
	return d > -0.0001f && d < 0.0001f;
}

#if defined(HAL_TARGET_PC98) || defined(HAL_TARGET_PCAT)
#undef floorf
static INLINE int floorf(float x)
{
	int i = (int)x;
	return (x < (float)i) ? i - 1 : i;
}

#undef ceilf
static INLINE int ceilf(float x)
{
	int i = (int)x;
	return (x > (float)i) ? i + 1 : i;
}

#undef lroundf
static INLINE int lroundf(float x)
{
	if (x >= 0.0f)
		return (int)(x + 0.5f);
	else
		return (int)(x - 0.5f);
}
#endif

static inline void
scanline_edge(
	struct scbuf *scbuf,
        float x1,
	float y1,
	float tx1,
	float ty1,
        float x2,
	float y2,
	float tx2,
	float ty2)
{
	int iy;

	/* Horizontal edge. */
	if (fp32_eq(y1, y2)) {
		iy = (int)lroundf(y1);
		if (iy < 0 || iy >= SC_LINES)
			return;

		if (x1 < x2) {
			if (x1 < scbuf[iy].sc_min_x) {
				scbuf[iy].sc_min_x  = (int)floorf(x1);
				scbuf[iy].sc_min_tx = tx1;
				scbuf[iy].sc_min_ty = ty1;
			}
			if (x2 > scbuf[iy].sc_max_x) {
				scbuf[iy].sc_max_x  = (int)ceilf(x2);
				scbuf[iy].sc_max_tx = tx2;
				scbuf[iy].sc_max_ty = ty2;
			}
		} else {
			if (x2 < scbuf[iy].sc_min_x) {
				scbuf[iy].sc_min_x  = (int)floorf(x2);
				scbuf[iy].sc_min_tx = tx2;
				scbuf[iy].sc_min_ty = ty2;
			}
			if (x1 > scbuf[iy].sc_max_x) {
				scbuf[iy].sc_max_x  = (int)ceilf(x1);
				scbuf[iy].sc_max_tx = tx1;
				scbuf[iy].sc_max_ty = ty1;
			}
		}
		return;
	}

	/* Make y1 <= y2 */
	if (y1 > y2) {
		float tmp;
		tmp = x1;  x1 = x2;  x2 = tmp;
		tmp = y1;  y1 = y2;  y2 = tmp;
		tmp = tx1; tx1 = tx2; tx2 = tmp;
		tmp = ty1; ty1 = ty2; ty2 = tmp;
	}

	/* Vertical edge. */
	if (fp32_eq(x1, x2)) {
		int ix = (int)ceilf(x1);
		for (iy = (int)ceilf(y1); iy <= (int)ceilf(y2); iy++) {
			float t, tx, ty;

			if (iy < 0)
				continue;
			if (iy >= SC_LINES)
				break;

			t = ((float)iy - y1) / (y2 - y1);
			if (t < 0)
				t = 0.0f;
			if (t > 1.0f)
				t = 1.0f;

			tx = tx1 + (tx2 - tx1) * t;
			ty = ty1 + (ty2 - ty1) * t;

			if (x1 < scbuf[iy].sc_min_x) {
				scbuf[iy].sc_min_x  = ix;
				scbuf[iy].sc_min_tx = tx;
				scbuf[iy].sc_min_ty = ty;
			}
			if (x1 > scbuf[iy].sc_max_x) {
				scbuf[iy].sc_max_x  = ix;
				scbuf[iy].sc_max_tx = tx;
				scbuf[iy].sc_max_ty = ty;
			}
		}
		return;
	}

	/* Non horizontal, non vertical. */
	for (iy = (int)ceilf(y1); iy <= (int)ceilf(y2); iy++) {
		float t, x, tx, ty;
		int ix;

		if (iy < 0)
			continue;
		if (iy >= SC_LINES)
			break;

		t = ((float)iy - y1) / (y2 - y1);
		if (t < 0)
			t = 0.0f;
		if (t > 1.0f)
			t = 1.0f;
		x  = x1  + (x2  - x1)  * t;

		tx = tx1 + (tx2 - tx1) * t;
		ty = ty1 + (ty2 - ty1) * t;

		ix  = (int)floorf(x);
		if (ix < scbuf[iy].sc_min_x) {
			scbuf[iy].sc_min_x  = ix;
			scbuf[iy].sc_min_tx = tx;
			scbuf[iy].sc_min_ty = ty;
		}
		if (ix > scbuf[iy].sc_max_x) {
			scbuf[iy].sc_max_x  = ix;
			scbuf[iy].sc_max_tx = tx;
			scbuf[iy].sc_max_ty = ty;
		}
	}
}

static inline void
scanline_conversion_body(
	struct scbuf *scbuf,
        float x1,
        float y1,
        float tx1,
        float ty1,
        float x2,
        float y2,
        float tx2,
        float ty2,
        float x3,
        float y3,
        float tx3,
        float ty3,
        float x4,
        float y4,
        float tx4,
        float ty4)
{
	int y;

	/* Initialize scanline buffers. */
	for (y = 0; y < SC_LINES; y++) {
		scbuf[y].sc_min_x  = INT_MAX;
		scbuf[y].sc_max_x  = INT_MIN;
		scbuf[y].sc_min_tx = 0.0f;
		scbuf[y].sc_max_tx = 0.0f;
		scbuf[y].sc_min_ty = 0.0f;
		scbuf[y].sc_max_ty = 0.0f;
	}

	/* Scan-convert the four edges of the quad */
	scanline_edge(scbuf, x1, y1, tx1, ty1, x2, y2, tx2, ty2);
	scanline_edge(scbuf, x2, y2, tx2, ty2, x3, y3, tx3, ty3);
	scanline_edge(scbuf, x3, y3, tx3, ty3, x1, y1, tx1, ty1);
	scanline_edge(scbuf, x3, y3, tx3, ty3, x4, y4, tx4, ty4);
	scanline_edge(scbuf, x4, y4, tx4, ty4, x2, y2, tx2, ty2);
}

void
scanline_conversion(
        float x1,
        float y1,
        float tx1,
        float ty1,
        float x2,
        float y2,
        float tx2,
        float ty2,
        float x3,
        float y3,
        float tx3,
        float ty3,
        float x4,
        float y4,
        float tx4,
        float ty4)
{
	scanline_conversion_body(scbuf1, x1, y1, tx1, ty1, x2, y2, tx2, ty2, x3, y3, tx3, ty3, x4, y4, tx4, ty4);
}

void
scanline_conversion_2(
        float x1,
        float y1,
        float tx1,
        float ty1,
        float x2,
        float y2,
        float tx2,
        float ty2,
        float x3,
        float y3,
        float tx3,
        float ty3,
        float x4,
        float y4,
        float tx4,
        float ty4)
{
	scanline_conversion_body(scbuf2, x1, y1, tx1, ty1, x2, y2, tx2, ty2, x3, y3, tx3, ty3, x4, y4, tx4, ty4);
}

/*
 * Clipping
 */

/*
 * Check for draw_image_*() parameters.
 */
bool
check_draw_image(
	struct hal_image *dst_image,
	int *dst_left,
	int *dst_top,
	struct hal_image *src_image,
	int *width,
	int *height,
	int *src_left,
	int *src_top,
	int alpha)
{
	/* Check the parameters. */
	assert(dst_image != NULL);
	assert(dst_image != src_image);
	assert(dst_image->width > 0 && dst_image->height > 0);
	assert(dst_image->pixels != NULL);
	assert(src_image != NULL);
	assert(src_image->width > 0 && src_image->height > 0);
	assert(src_image->pixels != NULL);

	/* Return false if no need for drawing. */
	if(alpha == 0 || *width == 0 || *height == 0)
		return false;
	if(!hal_clip_by_source(src_image->width, src_image->height, width, height, dst_left, dst_top, src_left, src_top))
		return false;
	if(!hal_clip_by_dest(dst_image->width, dst_image->height, width, height, dst_left, dst_top, src_left, src_top))
		return false;

	/* Need for draw. */
	return true;
}

/*
 * Clip by a source size.
 */
bool
hal_clip_by_source(
	int src_cx,	/* Source image width. */
	int src_cy,	/* Source image height. */
	int *cx,	/* [IN/OUT] Rect width. */
	int *cy,	/* [IN/OUT] Rect height. */
	int *dst_x,	/* [IN/OUT] Destination left. */
	int *dst_y,	/* [IN/OUT] Destination top. */
	int *src_x,	/* [IN/OUT] Source left. */
	int *src_y)	/* [IN/OUT] Source top. */
{
	/* Return false is the rect is completely out-of-range. */
	if(*src_x < 0 && -*src_x >= *cx)
		return false;	/* Left, out-of-range. */
	if(*src_y < 0 && -*src_y >= *cy)
		return false;	/* Top, out-of-range. */
	if(*src_x > 0 && *src_x >= src_cx)
		return false;	/* Right, out-of-range. */
	if(*src_y > 0 && *src_y >= src_cy)
		return false;	/* Bottom, out-of-range. */

	/* Cut a left out-of-range. */
	if(*src_x < 0) {
		*cx += *src_x;
		*dst_x -= *src_x;
		*src_x = 0;
	}

	/* Cut a top out-of-range. */
	if(*src_y < 0) {
		*cy += *src_y;
		*dst_y -= *src_y;
		*src_y = 0;
	}

	/* Cut a right out-of-range. */
	if(*src_x + *cx > src_cx)
		*cx = src_cx - *src_x;

	/* Cut a bottom out-of-range. */
	if(*src_y + *cy > src_cy)
		*cy = src_cy - *src_y;

	if (*cx <= 0 || *cy <= 0)
		return false;

	/* Succeeded. */
	return true;
}

/*
 * Clip by a destination size.
 */
bool
hal_clip_by_dest(
	int dst_cx,	/* Destination image width. */
	int dst_cy,	/* Destination image height. */
	int *cx,	/* [IN/OUT] Rect width. */
	int *cy,	/* [IN/OUT] Rect height. */
	int *dst_x,	/* [IN/OUT] Destination left. */
	int *dst_y,	/* [IN/OUT] Destination top. */
	int *src_x,	/* [IN/OUT] Source left. */
	int *src_y)	/* [IN/OUT] Source top. */
{
	/* Return false is the rect is completely out-of-range. */
	if(*dst_x < 0 && -*dst_x >= *cx)
		return false;	/* Left, out-of-range. */
	if(*dst_y < 0 && -*dst_y >= *cy)
		return false;	/* Top, out-of-range. */
	if(*dst_x > 0 && *dst_x >= dst_cx)
		return false;	/* Right, out-of-range. */
	if(*dst_y > 0 && *dst_y >= dst_cy)
		return false;	/* Bottom, out-of-range. */

	/* Cut a left out-of-range. */
	if(*dst_x < 0) {
		*cx += *dst_x;
		*src_x -= *dst_x;
		*dst_x = 0;
	}

	/* Cut a top out-of-range. */
	if(*dst_y < 0) {
		*cy += *dst_y;
		*src_y -= *dst_y;
		*dst_y = 0;
	}

	/* Cut a right out-of-range. */
	if(*dst_x + *cx > dst_cx)
		*cx = dst_cx - *dst_x;

	/* Cut a bottom out-of-range. */
	if(*dst_y + *cy > dst_cy)
		*cy = dst_cy - *dst_y;

	if (*cx <= 0 || *cy <= 0)
		return false;

	/* Succeeded. */
	return true;
}

/*
 * _aligned_malloc() is not supported on Windows 9x and 2000.
 * Just use malloc() instead on 9x and 2000.
 */
#if defined(HAL_TARGET_WINDOWS)

#include <windows.h>

static void *
wrap_aligned_malloc(
	size_t size,
	size_t align)
{
	static void *(__cdecl *p_aligned_malloc)(size_t, size_t) = NULL;
	static bool first_time = true;

	if (first_time) {
		HMODULE hModule = LoadLibraryA("msvcrt.dll");
		if (hModule != NULL)
			p_aligned_malloc = (void *)GetProcAddress(hModule, "_aligned_malloc");
		first_time = false;
	}

	/* Windows XP or later. */
	if (p_aligned_malloc != NULL)
		return p_aligned_malloc(size, align);

	/* Windows 9x or 2000. */
	return malloc(size);
}

static void
wrap_aligned_free(
	void *p)
{
	static void (*p_aligned_free)(void *) = NULL;
	static bool first_time = true;

	if (first_time) {
		HMODULE hModule = LoadLibraryA("msvcrt.dll");
		if (hModule != NULL)
			p_aligned_free = (void *)GetProcAddress(hModule, "_aligned_free");
		first_time = false;
	}

	/* Windows XP or later. */
	if (p_aligned_free != NULL) {
		p_aligned_free(p);
		return;
	}

	/* Windows 9x or 2000. */
	free(p);
}
#endif

/*
 * PNG
 */

#define PNG_DEBUG 3
#if defined(HAL_TARGET_WASM) || \
    defined(HAL_TARGET_ANDROID) || \
    (defined(HAL_TARGET_MACOS) && defined(HAL_USE_EXTDLL)) || \
    (defined(HAL_TARGET_POSIX) && defined(HAL_USE_EXTDLL)) || \
    defined(HAL_USE_QT)
#include <png.h>
#else
#include <png/png.h>
#endif

struct png_reader {
	const uint8_t *data;
	size_t size;
	size_t pos;
};

static void png_read_callback(png_structp png_ptr, png_bytep buf, png_size_t len);
static void png_warning_silent(png_structp png_ptr, png_const_charp warning_msg);

/*
 * Create an image with a PNG file.
 */
bool
hal_create_image_with_png(
	const uint8_t *data,
	size_t size,
	struct hal_image **img)
{
	struct png_reader reader;
	png_structp png_ptr;
	png_byte color_type, bit_depth;
	png_infop info_ptr;
	png_bytep *rows;
	int width;
	int height;
	int y;
	hal_pixel_t *pixels;

	/* Check a signature. */
	if (size < 8)
		return false;
	if (png_sig_cmp(data, 0, 8) != 0)
		return false;

	/* Create a png read struct. */
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL)
		return false;

	/* Create a png info struct. */
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		png_destroy_read_struct(&png_ptr, NULL, NULL);
		return false;
	}

	/* Return here if failed. */
	rows = NULL;
	if (setjmp(png_jmpbuf(png_ptr))) {
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		if (*img != NULL) {
			hal_destroy_image(*img);
			*img = NULL;
		}
		if (rows != NULL)
			free(rows);
		return false;
	}

	/* Read a header. */
	reader.data = data + 8;
	reader.size = size;
	reader.pos = 0;
	png_set_read_fn(png_ptr, &reader, png_read_callback);
	png_set_error_fn(png_ptr, NULL, NULL, png_warning_silent);
	png_set_sig_bytes(png_ptr, 8);
	png_read_info(png_ptr, info_ptr);

	/* Get metrics. */
	width = (int)png_get_image_width(png_ptr, info_ptr);
	height = (int)png_get_image_height(png_ptr, info_ptr);
	color_type = png_get_color_type(png_ptr, info_ptr);
	bit_depth = png_get_bit_depth(png_ptr, info_ptr);

	/* Convert palette to RGBA. */
	switch(color_type) {
	case PNG_COLOR_TYPE_GRAY:
		png_set_gray_to_rgb(png_ptr);
		png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
		png_read_update_info(png_ptr, info_ptr);
		break;
	case PNG_COLOR_TYPE_PALETTE:
#if !defined(HAL_ORDER_OPENGL)
		png_set_bgr(png_ptr);
#endif
		png_set_palette_to_rgb(png_ptr);
		png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
		png_read_update_info(png_ptr, info_ptr);
		break;
	case PNG_COLOR_TYPE_RGB:
#if !defined(HAL_ORDER_OPENGL)
		png_set_bgr(png_ptr);
#endif
		if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
			png_set_tRNS_to_alpha(png_ptr);
		} else {
			png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
			png_read_update_info(png_ptr, info_ptr);
		}
		break;
	case PNG_COLOR_TYPE_RGB_ALPHA:
#if !defined(HAL_ORDER_OPENGL)
		png_set_bgr(png_ptr);
#endif
		break;
	case PNG_COLOR_TYPE_GRAY_ALPHA:
		png_set_gray_to_rgb(png_ptr);
		png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
		png_read_update_info(png_ptr, info_ptr);
		break;
	default:
		return false;
	}

	/* 16-bit to 8-bit. */
	if (bit_depth == 16)
		png_set_strip_16(png_ptr);

	/* Allocate an image. */
	if (!hal_create_image(width, height, img))
		return false;

	/* Allocate a rows buffer. */
	rows = malloc(sizeof(png_bytep) * (size_t)height);
	if (rows == NULL) {
		hal_destroy_image(*img);
		hal_log_out_of_memory();
		return false;
	}
	assert(png_get_rowbytes(png_ptr, info_ptr) == (size_t)(width*4));

	/* Read an image. */
#ifdef _MSC_VER
#pragma warning(disable:6386)
#endif
	pixels = (*img)->pixels;
	for (y = 0; y < height; y++)
		rows[y] = (png_bytep)&pixels[width * y];
	png_read_image(png_ptr, rows);

	/* Cleanup. */
	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
	free(rows);

	return true;
}

static void
png_read_callback(
	png_structp png_ptr,
	png_bytep buf,
	png_size_t len)
{
	struct png_reader *reader;

	reader = png_get_io_ptr(png_ptr);

	if (reader->pos + len > reader->size)
		len = reader->size - reader->pos;

	memcpy(buf, reader->data + reader->pos, len);

	reader->pos += len;
}

static void
png_warning_silent(
	png_structp png_ptr,
	png_const_charp warning_msg)
{
	UNUSED_PARAMETER(png_ptr);
	UNUSED_PARAMETER(warning_msg);
}

/*
 * JPEG
 */

#if !defined(HAL_TARGET_PC98) && !defined(HAL_TARGET_PCAT)

#if defined(HAL_TARGET_WASM) || \
    (defined(HAL_TARGET_MACOS) && defined(HAL_USE_EXTDLL)) || \
    (defined(HAL_TARGET_POSIX) && defined(HAL_USE_EXTDLL)) || \
    defined(HAL_USE_QT)
#include <jpeglib.h>
#else
#include <jpeg/jpeglib.h>
#endif

/*
 * Create an image with a JPEG file.
 */
bool
hal_create_image_with_jpeg(
	const uint8_t *data,
	size_t size,
	struct hal_image **img)
{
	struct jpeg_decompress_struct jpeg;
	struct jpeg_error_mgr jerr;
	hal_pixel_t *p;
	unsigned char *line;
	unsigned int width, height, x, y;
	int components;

	/* Start decoding. */
	jpeg_create_decompress(&jpeg);
	jpeg_mem_src(&jpeg, data, size);
	jpeg.err = jpeg_std_error(&jerr);
	jpeg_read_header(&jpeg, TRUE);
	jpeg_start_decompress(&jpeg);

	/* Get metrics. */
	width = jpeg.output_width;
	height = jpeg.output_height;
	components = jpeg.out_color_components;
	if (components != 3) {
		jpeg_destroy_decompress(&jpeg);
		return false;
	}

	/* Create an image. */
	if (!hal_create_image((int)width, (int)height, img)) {
		jpeg_destroy_decompress(&jpeg);
		return false;
	}

	/* Allocate a line buffer. */
	line = malloc(width * height * 3);
	if (line == NULL) {
		hal_log_out_of_memory();
		jpeg_destroy_decompress(&jpeg);
		hal_destroy_image(*img);
		*img = NULL;
		return false;
	}

	/* Decode each line. */
	p = (*img)->pixels;
#if defined(HAL_ORDER_OPENGL)
	for (y = 0; y < height; y++) {
		jpeg_read_scanlines(&jpeg, &line, 1);
		for (x = 0; x < width; x++) {
			*p++ = hal_make_pixel(255,
					      line[x * 3 + 2],
					      line[x * 3 + 1],
					      line[x * 3 + 0]);
		}
	}
#else
	for (y = 0; y < height; y++) {
		jpeg_read_scanlines(&jpeg, &line, 1);
		for (x = 0; x < width; x++) {
			*p++ = hal_make_pixel(255,
					      line[x * 3],
					      line[x * 3 + 1],
					      line[x * 3 + 2]);
		}
	}
#endif

	/* Cleanup. */
	free(line);
	jpeg_destroy_decompress(&jpeg);

	return true;
}

#else

bool
hal_create_image_with_jpeg(
	const uint8_t *data,
	size_t size,
	struct hal_image **img)
{
	UNUSED_PARAMETER(data);
	UNUSED_PARAMETER(size);
	UNUSED_PARAMETER(img);

	hal_log_error("JPEG is not supported.");
	return false;
}

#endif /* !defined(HAL_TARGET_PC98) && !defined(HAL_TARGET_PCAT) */

/*
 * WebP
 */

#if !defined(HAL_TARGET_PC98) && !defined(HAL_TARGET_PCAT)

#include <webp/decode.h>

/*
 * Create an image with a WebP file.
 */
bool
hal_create_image_with_webp(
	const uint8_t *data,
	size_t size,
	struct hal_image **img)
{
	hal_pixel_t *p;
	uint8_t *pixels;
	int width, height;
	int x, y;

	/* Get metrics. */
	if (!WebPGetInfo(data, size, &width, &height))
		return false;

	/* Create an image. */
	if (!hal_create_image(width, height, img))
		return false;

	/* Do decoding. */
	pixels = WebPDecodeRGBA(data, size, &width, &height);
	if (pixels == NULL) {
		hal_destroy_image(*img);
		*img = NULL;
		return false;
	}

	/* Copy pixels. */
	p = (*img)->pixels;
#if defined(HAL_ORDER_OPENGL)
	/* Use RGBA as is. */
	for (y = 0; y < height; y++) {
		for (x = 0; x < width; x++) {
			*p++ = hal_make_pixel(pixels[y * width * 4 + x * 4 + 3],
					      pixels[y * width * 4 + x * 4 + 0],
					      pixels[y * width * 4 + x * 4 + 1],
					      pixels[y * width * 4 + x * 4 + 2]);
		}
	}
#else
	/* Reverse the pixel order for OpenGL. (RGB -> BGR) */
	for (y = 0; y < height; y++) {
		for (x = 0; x < width; x++) {
			*p++ = hal_make_pixel(pixels[y * width * 4 + x * 4 + 3],
					      pixels[y * width * 4 + x * 4 + 2],
					      pixels[y * width * 4 + x * 4 + 1],
					      pixels[y * width * 4 + x * 4 + 0]);
		}
	}
#endif

	/* Cleanup. */
	WebPFree(pixels);

	return true;
}

#else

bool
hal_create_image_with_webp(
	const uint8_t *data,
	size_t size,
	struct hal_image **img)
{
	UNUSED_PARAMETER(data);
	UNUSED_PARAMETER(size);
	UNUSED_PARAMETER(img);

	hal_log_error("WebP is not supported.");
	return false;
}

#endif /* !defined(HAL_TARGET_PC98) && !defined(HAL_TARGET_PCAT) */
