mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-28 04:34:28 +01:00
1229 lines
33 KiB
C
1229 lines
33 KiB
C
#include "global.h"
|
|
#include "image_processing_effects.h"
|
|
#include "contest_painting.h"
|
|
#include "constants/rgb.h"
|
|
|
|
// IWRAM common
|
|
u8 gCanvasColumnStart;
|
|
u16 *gCanvasPixels;
|
|
u8 gCanvasRowEnd;
|
|
u8 gCanvasHeight;
|
|
u8 gCanvasColumnEnd;
|
|
u8 gCanvasRowStart;
|
|
u8 gCanvasMonPersonality;
|
|
u8 gCanvasWidth;
|
|
u16 *gCanvasPalette;
|
|
u16 gCanvasPaletteStart;
|
|
|
|
static void ApplyImageEffect_Pointillism(void);
|
|
static void ApplyImageEffect_Blur(void);
|
|
static void ApplyImageEffect_BlackOutline(void);
|
|
static void ApplyImageEffect_Invert(void);
|
|
static void ApplyImageEffect_BlackAndWhite(void);
|
|
static void ApplyImageEffect_BlurRight(void);
|
|
static void ApplyImageEffect_BlurDown(void);
|
|
static void ApplyImageEffect_Shimmer(void);
|
|
static void ApplyImageEffect_Grayscale(void);
|
|
static void ApplyImageEffect_PersonalityColor(u8);
|
|
static void ApplyImageEffect_RedChannelGrayscale(u8);
|
|
static void ApplyImageEffect_RedChannelGrayscaleHighlight(u8);
|
|
static void AddPointillismPoints(u16);
|
|
static u16 ConvertColorToGrayscale(u16 *);
|
|
static u16 QuantizePixel_Blur(u16 *, u16 *, u16 *);
|
|
static u16 QuantizePixel_PersonalityColor(u16 *, u8);
|
|
static u16 QuantizePixel_BlackAndWhite(u16 *);
|
|
static u16 QuantizePixel_BlackOutline(u16 *, u16 *);
|
|
static u16 QuantizePixel_Invert(u16 *);
|
|
static u16 QuantizePixel_BlurHard(u16 *, u16 *, u16 *);
|
|
static u16 QuantizePixel_MotionBlur(u16 *, u16 *);
|
|
static u16 GetColorFromPersonality(u8);
|
|
static void QuantizePalette_Standard(bool8);
|
|
static void SetPresetPalette_PrimaryColors(void);
|
|
static void QuantizePalette_PrimaryColors(void);
|
|
static void SetPresetPalette_Grayscale(void);
|
|
static void QuantizePalette_Grayscale(void);
|
|
static void SetPresetPalette_GrayscaleSmall(void);
|
|
static void QuantizePalette_GrayscaleSmall(void);
|
|
static void SetPresetPalette_BlackAndWhite(void);
|
|
static void QuantizePalette_BlackAndWhite(void);
|
|
static u16 QuantizePixel_Standard(u16 *);
|
|
static u16 QuantizePixel_GrayscaleSmall(u16 *);
|
|
static u16 QuantizePixel_Grayscale(u16 *);
|
|
static u16 QuantizePixel_PrimaryColors(u16 *);
|
|
|
|
#define MAX_DIMENSION 64
|
|
|
|
#include "data/pointillism_points.h"
|
|
|
|
void ApplyImageProcessingEffects(struct ImageProcessingContext *context)
|
|
{
|
|
gCanvasPixels = context->canvasPixels;
|
|
gCanvasMonPersonality = context->personality;
|
|
gCanvasColumnStart = context->columnStart;
|
|
gCanvasRowStart = context->rowStart;
|
|
gCanvasColumnEnd = context->columnEnd;
|
|
gCanvasRowEnd = context->rowEnd;
|
|
gCanvasWidth = context->canvasWidth;
|
|
gCanvasHeight = context->canvasHeight;
|
|
|
|
switch (context->effect)
|
|
{
|
|
case IMAGE_EFFECT_POINTILLISM:
|
|
ApplyImageEffect_Pointillism();
|
|
break;
|
|
case IMAGE_EFFECT_BLUR:
|
|
ApplyImageEffect_Blur();
|
|
break;
|
|
case IMAGE_EFFECT_OUTLINE_COLORED:
|
|
ApplyImageEffect_BlackOutline();
|
|
ApplyImageEffect_PersonalityColor(gCanvasMonPersonality);
|
|
break;
|
|
case IMAGE_EFFECT_INVERT_BLACK_WHITE:
|
|
ApplyImageEffect_BlackOutline();
|
|
ApplyImageEffect_Invert();
|
|
ApplyImageEffect_BlackAndWhite();
|
|
case IMAGE_EFFECT_INVERT:
|
|
ApplyImageEffect_Invert();
|
|
break;
|
|
case IMAGE_EFFECT_THICK_BLACK_WHITE:
|
|
ApplyImageEffect_BlackOutline();
|
|
ApplyImageEffect_BlurRight();
|
|
ApplyImageEffect_BlurRight();
|
|
ApplyImageEffect_BlurDown();
|
|
ApplyImageEffect_BlackAndWhite();
|
|
break;
|
|
case IMAGE_EFFECT_SHIMMER:
|
|
ApplyImageEffect_Shimmer();
|
|
break;
|
|
case IMAGE_EFFECT_OUTLINE:
|
|
ApplyImageEffect_BlackOutline();
|
|
break;
|
|
case IMAGE_EFFECT_BLUR_RIGHT:
|
|
ApplyImageEffect_BlurRight();
|
|
break;
|
|
case IMAGE_EFFECT_BLUR_DOWN:
|
|
ApplyImageEffect_BlurDown();
|
|
break;
|
|
case IMAGE_EFFECT_GRAYSCALE_LIGHT:
|
|
ApplyImageEffect_Grayscale();
|
|
ApplyImageEffect_RedChannelGrayscale(3);
|
|
break;
|
|
case IMAGE_EFFECT_CHARCOAL:
|
|
ApplyImageEffect_BlackOutline();
|
|
ApplyImageEffect_BlurRight();
|
|
ApplyImageEffect_BlurDown();
|
|
ApplyImageEffect_BlackAndWhite();
|
|
ApplyImageEffect_Blur();
|
|
ApplyImageEffect_Blur();
|
|
ApplyImageEffect_RedChannelGrayscale(2);
|
|
ApplyImageEffect_RedChannelGrayscaleHighlight(4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_RedChannelGrayscale(u8 delta)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
{
|
|
// Gets the grayscale value, based on the pixel's red channel.
|
|
// Also adds a delta to skew lighter or darker.
|
|
u8 grayValue = (*pixel & RGB_RED);
|
|
grayValue += delta;
|
|
if (grayValue > 31)
|
|
grayValue = 31;
|
|
|
|
*pixel = RGB2(grayValue, grayValue, grayValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_RedChannelGrayscaleHighlight(u8 highlight)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
{
|
|
u8 grayValue = (*pixel & RGB_RED);
|
|
if (grayValue > 31 - highlight)
|
|
grayValue = 31 - (highlight >> 1);
|
|
|
|
*pixel = RGB2(grayValue, grayValue, grayValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_Pointillism(void)
|
|
{
|
|
u32 i;
|
|
for (i = 0; i < ARRAY_COUNT(sPointillismPoints); i++)
|
|
AddPointillismPoints(i);
|
|
}
|
|
|
|
static void ApplyImageEffect_Grayscale(void)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
*pixel = ConvertColorToGrayscale(pixel);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_Blur(void)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (i = 0; i < gCanvasColumnEnd; i++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[gCanvasRowStart * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart + i];
|
|
u16 prevPixel = *pixel;
|
|
|
|
j = 1;
|
|
pixel += gCanvasWidth;
|
|
while (j < gCanvasRowEnd - 1)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
{
|
|
*pixel = QuantizePixel_Blur(&prevPixel, pixel, pixel + gCanvasWidth);
|
|
prevPixel = *pixel;
|
|
}
|
|
|
|
j++;
|
|
pixel += gCanvasWidth;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_PersonalityColor(u8 personality)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
*pixel = QuantizePixel_PersonalityColor(pixel, personality);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_BlackAndWhite(void)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
*pixel = QuantizePixel_BlackAndWhite(pixel);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_BlackOutline(void)
|
|
{
|
|
u8 i, j;
|
|
u16 *pixel;
|
|
|
|
// Handle top row of pixels first.
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
pixel = &pixelRow[gCanvasColumnStart];
|
|
*pixel = QuantizePixel_BlackOutline(pixel, pixel + 1);
|
|
for (i = 1, pixel++; i < gCanvasColumnEnd - 1; i++, pixel++)
|
|
{
|
|
*pixel = QuantizePixel_BlackOutline(pixel, pixel + 1);
|
|
*pixel = QuantizePixel_BlackOutline(pixel, pixel - 1);
|
|
}
|
|
|
|
*pixel = QuantizePixel_BlackOutline(pixel, pixel - 1);
|
|
}
|
|
|
|
// Handle each column from left to right.
|
|
for (i = 0; i < gCanvasColumnEnd; i++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[gCanvasRowStart * gCanvasWidth];
|
|
pixel = &pixelRow[gCanvasColumnStart + i];
|
|
*pixel = QuantizePixel_BlackOutline(pixel, pixel + gCanvasWidth);
|
|
for (j = 1, pixel += gCanvasWidth; j < gCanvasRowEnd - 1; j++, pixel += gCanvasWidth)
|
|
{
|
|
*pixel = QuantizePixel_BlackOutline(pixel, pixel + gCanvasWidth);
|
|
*pixel = QuantizePixel_BlackOutline(pixel, pixel - gCanvasWidth);
|
|
}
|
|
|
|
*pixel = QuantizePixel_BlackOutline(pixel, pixel - gCanvasWidth);
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_Invert(void)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
*pixel = QuantizePixel_Invert(pixel);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_Shimmer(void)
|
|
{
|
|
u8 i, j;
|
|
u16 *pixel;
|
|
u16 prevPixel;
|
|
|
|
// First, invert all of the colors.
|
|
pixel = gCanvasPixels;
|
|
for (i = 0; i < MAX_DIMENSION; i++)
|
|
{
|
|
for (j = 0; j < MAX_DIMENSION; j++, pixel++)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
*pixel = QuantizePixel_Invert(pixel);
|
|
}
|
|
}
|
|
|
|
// Blur the pixels twice.
|
|
for (j = 0; j < MAX_DIMENSION; j++)
|
|
{
|
|
pixel = &gCanvasPixels[j];
|
|
prevPixel = *pixel;
|
|
*pixel = RGB_ALPHA;
|
|
for (i = 1, pixel += MAX_DIMENSION; i < MAX_DIMENSION - 1; i++, pixel += MAX_DIMENSION)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
{
|
|
*pixel = QuantizePixel_BlurHard(&prevPixel, pixel, pixel + MAX_DIMENSION);
|
|
prevPixel = *pixel;
|
|
}
|
|
}
|
|
|
|
*pixel = RGB_ALPHA;
|
|
pixel = &gCanvasPixels[j];
|
|
prevPixel = *pixel;
|
|
*pixel = RGB_ALPHA;
|
|
for (i = 1, pixel += MAX_DIMENSION; i < MAX_DIMENSION - 1; i++, pixel += MAX_DIMENSION)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
{
|
|
*pixel = QuantizePixel_BlurHard(&prevPixel, pixel, pixel + MAX_DIMENSION);
|
|
prevPixel = *pixel;
|
|
}
|
|
}
|
|
|
|
*pixel = RGB_ALPHA;
|
|
}
|
|
|
|
// Finally, invert colors back to the original color space.
|
|
// The above blur causes the outline areas to darken, which makes
|
|
// this inversion give the effect of light outlines.
|
|
pixel = gCanvasPixels;
|
|
for (i = 0; i < MAX_DIMENSION; i++)
|
|
{
|
|
for (j = 0; j < MAX_DIMENSION; j++, pixel++)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
*pixel = QuantizePixel_Invert(pixel);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_BlurRight(void)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
u16 prevPixel = *pixel;
|
|
for (i = 1, pixel++; i < gCanvasColumnEnd - 1; i++, pixel++)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
{
|
|
*pixel = QuantizePixel_MotionBlur(&prevPixel, pixel);
|
|
prevPixel = *pixel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_BlurDown(void)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (i = 0; i < gCanvasColumnEnd; i++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[gCanvasRowStart * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart + i];
|
|
u16 prevPixel = *pixel;
|
|
for (j = 1, pixel += gCanvasWidth; j < gCanvasRowEnd - 1; j++, pixel += gCanvasWidth)
|
|
{
|
|
if (!IS_ALPHA(*pixel))
|
|
{
|
|
*pixel = QuantizePixel_MotionBlur(&prevPixel, pixel);
|
|
prevPixel = *pixel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct PointillismPoint
|
|
{
|
|
u8 column;
|
|
u8 row;
|
|
u16 delta;
|
|
};
|
|
|
|
static void AddPointillismPoints(u16 point)
|
|
{
|
|
u8 i;
|
|
bool8 offsetDownLeft;
|
|
u8 colorType;
|
|
struct PointillismPoint points[6];
|
|
|
|
points[0].column = sPointillismPoints[point][0];
|
|
points[0].row = sPointillismPoints[point][1];
|
|
points[0].delta = GET_POINT_DELTA(sPointillismPoints[point][2]);
|
|
|
|
colorType = GET_POINT_COLOR_TYPE(sPointillismPoints[point][2]);
|
|
offsetDownLeft = GET_POINT_OFFSET_DL(sPointillismPoints[point][2]);
|
|
for (i = 1; i < points[0].delta; i++)
|
|
{
|
|
if (!offsetDownLeft)
|
|
{
|
|
points[i].column = points[0].column - i;
|
|
points[i].row = points[0].row + i;
|
|
}
|
|
else
|
|
{
|
|
points[i].column = points[0].column + 1;
|
|
points[i].row = points[0].row - 1;
|
|
}
|
|
|
|
if (points[i].column >= MAX_DIMENSION || points[i].row >= MAX_DIMENSION)
|
|
{
|
|
points[0].delta = i - 1;
|
|
break;
|
|
}
|
|
|
|
points[i].delta = points[0].delta - i;
|
|
}
|
|
|
|
for (i = 0; i < points[0].delta; i++)
|
|
{
|
|
u16 *pixel = &gCanvasPixels[points[i].row * MAX_DIMENSION] + points[i].column;
|
|
|
|
if (!IS_ALPHA(*pixel))
|
|
{
|
|
u16 red = GET_R(*pixel);
|
|
u16 green = GET_G(*pixel);
|
|
u16 blue = GET_B(*pixel);
|
|
|
|
switch (colorType)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
switch (GET_POINT_DELTA(sPointillismPoints[point][2]) % 3)
|
|
{
|
|
case 0:
|
|
if (red >= points[i].delta)
|
|
red -= points[i].delta;
|
|
else
|
|
red = 0;
|
|
break;
|
|
case 1:
|
|
if (green >= points[i].delta)
|
|
green -= points[i].delta;
|
|
else
|
|
green = 0;
|
|
break;
|
|
case 2:
|
|
if (blue >= points[i].delta)
|
|
blue -= points[i].delta;
|
|
else
|
|
blue = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
red += points[i].delta;
|
|
green += points[i].delta;
|
|
blue += points[i].delta;
|
|
if (red > 31)
|
|
red = 31;
|
|
if (green > 31)
|
|
green = 31;
|
|
if (blue > 31)
|
|
blue = 31;
|
|
break;
|
|
}
|
|
|
|
*pixel = RGB2(red, green, blue);
|
|
}
|
|
}
|
|
}
|
|
|
|
static u16 ConvertColorToGrayscale(u16 *color)
|
|
{
|
|
s32 clr = *color;
|
|
s32 r = GET_R(clr);
|
|
s32 g = GET_G(clr);
|
|
s32 b = GET_B(clr);
|
|
s32 gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
|
|
return RGB2(gray, gray, gray);
|
|
}
|
|
|
|
// The dark colors are the colored edges of the Cool painting effect.
|
|
// Everything else is white.
|
|
static u16 QuantizePixel_PersonalityColor(u16 *color, u8 personality)
|
|
{
|
|
u16 red = GET_R(*color);
|
|
u16 green = GET_G(*color);
|
|
u16 blue = GET_B(*color);
|
|
|
|
if (red < 17 && green < 17 && blue < 17)
|
|
return GetColorFromPersonality(personality);
|
|
else
|
|
return RGB_WHITE;
|
|
}
|
|
|
|
// Based on the given value, which comes from the first 8 bits of
|
|
// the mon's personality value, return a color.
|
|
static u16 GetColorFromPersonality(u8 personality)
|
|
{
|
|
u16 red = 0;
|
|
u16 green = 0;
|
|
u16 blue = 0;
|
|
u8 strength = (personality / 6) % 3;
|
|
u8 colorType = personality % 6;
|
|
|
|
switch (colorType)
|
|
{
|
|
case 0:
|
|
// Teal color
|
|
green = 21 - strength;
|
|
blue = green;
|
|
red = 0;
|
|
break;
|
|
case 1:
|
|
// Yellow color
|
|
blue = 0;
|
|
red = 21 - strength;
|
|
green = red;
|
|
break;
|
|
case 2:
|
|
// Purple color
|
|
blue = 21 - strength;
|
|
green = 0;
|
|
red = blue;
|
|
break;
|
|
case 3:
|
|
// Red color
|
|
blue = 0;
|
|
green = 0;
|
|
red = 23 - strength;
|
|
break;
|
|
case 4:
|
|
// Blue color
|
|
blue = 23 - strength;
|
|
green = 0;
|
|
red = 0;
|
|
break;
|
|
case 5:
|
|
// Green color
|
|
blue = 0;
|
|
green = 23 - strength;
|
|
red = 0;
|
|
break;
|
|
}
|
|
|
|
return RGB2(red, green, blue);
|
|
}
|
|
|
|
static u16 QuantizePixel_BlackAndWhite(u16 *color)
|
|
{
|
|
u16 red = GET_R(*color);
|
|
u16 green = GET_G(*color);
|
|
u16 blue = GET_B(*color);
|
|
|
|
if (red < 17 && green < 17 && blue < 17)
|
|
return RGB_BLACK;
|
|
else
|
|
return RGB_WHITE;
|
|
}
|
|
|
|
static u16 QuantizePixel_BlackOutline(u16 *pixelA, u16 *pixelB)
|
|
{
|
|
if (*pixelA != RGB_BLACK)
|
|
{
|
|
if (IS_ALPHA(*pixelA))
|
|
return RGB_ALPHA;
|
|
if (IS_ALPHA(*pixelB))
|
|
return RGB_BLACK;
|
|
|
|
return *pixelA;
|
|
}
|
|
|
|
return RGB_BLACK;
|
|
}
|
|
|
|
static u16 QuantizePixel_Invert(u16 *color)
|
|
{
|
|
u16 red = GET_R(*color);
|
|
u16 green = GET_G(*color);
|
|
u16 blue = GET_B(*color);
|
|
|
|
red = 31 - red;
|
|
green = 31 - green;
|
|
blue = 31 - blue;
|
|
|
|
return RGB2(red, green, blue);
|
|
}
|
|
|
|
static u16 QuantizePixel_MotionBlur(u16 *prevPixel, u16 *curPixel)
|
|
{
|
|
u16 pixelChannels[2][3];
|
|
u16 diffs[3];
|
|
u8 i;
|
|
u16 largestDiff;
|
|
u16 red, green, blue;
|
|
|
|
if (*prevPixel == *curPixel)
|
|
return *curPixel;
|
|
|
|
pixelChannels[0][0] = GET_R(*prevPixel);
|
|
pixelChannels[0][1] = GET_G(*prevPixel);
|
|
pixelChannels[0][2] = GET_B(*prevPixel);
|
|
pixelChannels[1][0] = GET_R(*curPixel);
|
|
pixelChannels[1][1] = GET_G(*curPixel);
|
|
pixelChannels[1][2] = GET_B(*curPixel);
|
|
|
|
// Don't blur light colors.
|
|
if (pixelChannels[0][0] > 25 && pixelChannels[0][1] > 25 && pixelChannels[0][2] > 25)
|
|
return *curPixel;
|
|
if (pixelChannels[1][0] > 25 && pixelChannels[1][1] > 25 && pixelChannels[1][2] > 25)
|
|
return *curPixel;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (pixelChannels[0][i] > pixelChannels[1][i])
|
|
diffs[i] = pixelChannels[0][i] - pixelChannels[1][i];
|
|
else
|
|
diffs[i] = pixelChannels[1][i] - pixelChannels[0][i];
|
|
}
|
|
|
|
// Find the largest diff of any of the color channels.
|
|
if (diffs[0] >= diffs[1])
|
|
{
|
|
if (diffs[0] >= diffs[2])
|
|
largestDiff = diffs[0];
|
|
else if (diffs[1] >= diffs[2])
|
|
largestDiff = diffs[1];
|
|
else
|
|
largestDiff = diffs[2];
|
|
}
|
|
else
|
|
{
|
|
if (diffs[1] >= diffs[2])
|
|
largestDiff = diffs[1];
|
|
else if (diffs[2] >= diffs[0])
|
|
largestDiff = diffs[2];
|
|
else
|
|
largestDiff = diffs[0];
|
|
}
|
|
|
|
red = (pixelChannels[1][0] * (31 - largestDiff / 2)) / 31;
|
|
green = (pixelChannels[1][1] * (31 - largestDiff / 2)) / 31;
|
|
blue = (pixelChannels[1][2] * (31 - largestDiff / 2)) / 31;
|
|
return RGB2(red, green, blue);
|
|
}
|
|
|
|
static u16 QuantizePixel_Blur(u16 *prevPixel, u16 *curPixel, u16 *nextPixel)
|
|
{
|
|
u16 red, green, blue;
|
|
u16 prevAvg, curAvg, nextAvg;
|
|
u16 prevDiff, nextDiff;
|
|
u32 diff;
|
|
u16 factor;
|
|
|
|
if (*prevPixel == *curPixel && *nextPixel == *curPixel)
|
|
return *curPixel;
|
|
|
|
red = GET_R(*curPixel);
|
|
green = GET_G(*curPixel);
|
|
blue = GET_B(*curPixel);
|
|
|
|
prevAvg = (GET_R(*prevPixel) + GET_G(*prevPixel) + GET_B(*prevPixel)) / 3;
|
|
curAvg = (GET_R(*curPixel) + GET_G(*curPixel) + GET_B(*curPixel)) / 3;
|
|
nextAvg = (GET_R(*nextPixel) + GET_G(*nextPixel) + GET_B(*nextPixel)) / 3;
|
|
|
|
if (prevAvg == curAvg && nextAvg == curAvg)
|
|
return *curPixel;
|
|
|
|
if (prevAvg > curAvg)
|
|
prevDiff = prevAvg - curAvg;
|
|
else
|
|
prevDiff = curAvg - prevAvg;
|
|
|
|
if (nextAvg > curAvg)
|
|
nextDiff = nextAvg - curAvg;
|
|
else
|
|
nextDiff = curAvg - nextAvg;
|
|
|
|
if (prevDiff >= nextDiff)
|
|
diff = prevDiff;
|
|
else
|
|
diff = nextDiff;
|
|
|
|
factor = 31 - diff / 2;
|
|
red = (red * factor) / 31;
|
|
green = (green * factor) / 31;
|
|
blue = (blue * factor) / 31;
|
|
return RGB2(red, green, blue);
|
|
}
|
|
|
|
static u16 QuantizePixel_BlurHard(u16 *prevPixel, u16 *curPixel, u16 *nextPixel)
|
|
{
|
|
u16 red, green, blue;
|
|
u16 prevAvg, curAvg, nextAvg;
|
|
u16 prevDiff, nextDiff;
|
|
u32 diff;
|
|
u16 factor;
|
|
|
|
if (*prevPixel == *curPixel && *nextPixel == *curPixel)
|
|
return *curPixel;
|
|
|
|
red = GET_R(*curPixel);
|
|
green = GET_G(*curPixel);
|
|
blue = GET_B(*curPixel);
|
|
|
|
prevAvg = (GET_R(*prevPixel) + GET_G(*prevPixel) + GET_B(*prevPixel)) / 3;
|
|
curAvg = (GET_R(*curPixel) + GET_G(*curPixel) + GET_B(*curPixel)) / 3;
|
|
nextAvg = (GET_R(*nextPixel) + GET_G(*nextPixel) + GET_B(*nextPixel)) / 3;
|
|
|
|
if (prevAvg == curAvg && nextAvg == curAvg)
|
|
return *curPixel;
|
|
|
|
if (prevAvg > curAvg)
|
|
prevDiff = prevAvg - curAvg;
|
|
else
|
|
prevDiff = curAvg - prevAvg;
|
|
|
|
if (nextAvg > curAvg)
|
|
nextDiff = nextAvg - curAvg;
|
|
else
|
|
nextDiff = curAvg - nextAvg;
|
|
|
|
if (prevDiff >= nextDiff)
|
|
diff = prevDiff;
|
|
else
|
|
diff = nextDiff;
|
|
|
|
factor = 31 - diff;
|
|
red = (red * factor) / 31;
|
|
green = (green * factor) / 31;
|
|
blue = (blue * factor) / 31;
|
|
return RGB2(red, green, blue);
|
|
}
|
|
|
|
void ConvertImageProcessingToGBA(struct ImageProcessingContext *context)
|
|
{
|
|
u16 i, j, k;
|
|
u16 *src, *dest, *src_, *dest_;
|
|
u16 width, height;
|
|
|
|
width = context->canvasWidth >> 3;
|
|
height = context->canvasHeight >> 3;
|
|
src_ = context->canvasPixels;
|
|
dest_ = context->dest;
|
|
|
|
if (context->var_16 == 2)
|
|
{
|
|
for (i = 0; i < height; i++)
|
|
{
|
|
for (j = 0; j < width; j++)
|
|
{
|
|
for (k = 0; k < 8; k++)
|
|
{
|
|
dest = dest_ + ((i * width + j) << 5) + (k << 2);
|
|
src = src_ + ((((i << 3) + k) << 3) * width) + (j << 3);
|
|
|
|
dest[0] = src[0] | (src[1] << 8);
|
|
dest[1] = src[2] | (src[3] << 8);
|
|
dest[2] = src[4] | (src[5] << 8);
|
|
dest[3] = src[6] | (src[7] << 8);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < height; i++)
|
|
{
|
|
for (j = 0; j < width; j++)
|
|
{
|
|
for (k = 0; k < 8; k++)
|
|
{
|
|
dest = dest_ + ((i * width + j) << 4) + (k << 1);
|
|
src = src_ + ((((i << 3) + k) << 3) * width) + (j << 3);
|
|
|
|
dest[0] = src[0] | (src[1] << 4) | (src[2] << 8) | (src[3] << 12);
|
|
dest[1] = src[4] | (src[5] << 4) | (src[6] << 8) | (src[7] << 12);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ApplyImageProcessingQuantization(struct ImageProcessingContext *context)
|
|
{
|
|
gCanvasPaletteStart = context->paletteStart * 16;
|
|
gCanvasPalette = &context->canvasPalette[gCanvasPaletteStart];
|
|
gCanvasPixels = context->canvasPixels;
|
|
gCanvasColumnStart = context->columnStart;
|
|
gCanvasRowStart = context->rowStart;
|
|
gCanvasColumnEnd = context->columnEnd;
|
|
gCanvasRowEnd = context->rowEnd;
|
|
gCanvasWidth = context->canvasWidth;
|
|
gCanvasHeight = context->canvasHeight;
|
|
|
|
switch (context->quantizeEffect)
|
|
{
|
|
case QUANTIZE_EFFECT_STANDARD:
|
|
QuantizePalette_Standard(FALSE);
|
|
break;
|
|
case QUANTIZE_EFFECT_STANDARD_LIMITED_COLORS:
|
|
QuantizePalette_Standard(TRUE);
|
|
break;
|
|
case QUANTIZE_EFFECT_PRIMARY_COLORS:
|
|
SetPresetPalette_PrimaryColors();
|
|
QuantizePalette_PrimaryColors();
|
|
break;
|
|
case QUANTIZE_EFFECT_GRAYSCALE:
|
|
SetPresetPalette_Grayscale();
|
|
QuantizePalette_Grayscale();
|
|
break;
|
|
case QUANTIZE_EFFECT_GRAYSCALE_SMALL:
|
|
SetPresetPalette_GrayscaleSmall();
|
|
QuantizePalette_GrayscaleSmall();
|
|
break;
|
|
case QUANTIZE_EFFECT_BLACK_WHITE:
|
|
SetPresetPalette_BlackAndWhite();
|
|
QuantizePalette_BlackAndWhite();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void SetPresetPalette_PrimaryColors(void)
|
|
{
|
|
gCanvasPalette[0] = RGB_BLACK;
|
|
gCanvasPalette[1] = RGB(6, 6, 6);
|
|
gCanvasPalette[2] = RGB(29, 29, 29);
|
|
gCanvasPalette[3] = RGB(11, 11, 11);
|
|
gCanvasPalette[4] = RGB(29, 6, 6);
|
|
gCanvasPalette[5] = RGB(6, 29, 6);
|
|
gCanvasPalette[6] = RGB(6, 6, 29);
|
|
gCanvasPalette[7] = RGB(29, 29, 6);
|
|
gCanvasPalette[8] = RGB(29, 6, 29);
|
|
gCanvasPalette[9] = RGB(6, 29, 29);
|
|
gCanvasPalette[10] = RGB(29, 11, 6);
|
|
gCanvasPalette[11] = RGB(11, 29, 6);
|
|
gCanvasPalette[12] = RGB(6, 11, 29);
|
|
gCanvasPalette[13] = RGB(29, 6, 11);
|
|
gCanvasPalette[14] = RGB(6, 29, 11);
|
|
gCanvasPalette[15] = RGB(11, 6, 29);
|
|
}
|
|
|
|
static void SetPresetPalette_BlackAndWhite(void)
|
|
{
|
|
gCanvasPalette[0] = RGB_BLACK;
|
|
gCanvasPalette[1] = RGB_BLACK;
|
|
gCanvasPalette[2] = RGB_WHITE;
|
|
}
|
|
|
|
static void SetPresetPalette_GrayscaleSmall(void)
|
|
{
|
|
u8 i;
|
|
|
|
gCanvasPalette[0] = RGB_BLACK;
|
|
gCanvasPalette[1] = RGB_BLACK;
|
|
for (i = 0; i < 14; i++)
|
|
gCanvasPalette[i + 2] = RGB2(2 * (i + 2), 2 * (i + 2), 2 * (i + 2));
|
|
}
|
|
|
|
static void SetPresetPalette_Grayscale(void)
|
|
{
|
|
u8 i;
|
|
|
|
gCanvasPalette[0] = RGB_BLACK;
|
|
for (i = 0; i < 32; i++)
|
|
gCanvasPalette[i + 1] = RGB2(i, i, i);
|
|
}
|
|
|
|
static void QuantizePalette_Standard(bool8 useLimitedPalette)
|
|
{
|
|
u8 i, j;
|
|
u16 maxIndex;
|
|
|
|
maxIndex = 0xDF;
|
|
if (!useLimitedPalette)
|
|
maxIndex = 0xFF;
|
|
|
|
for (i = 0; i < maxIndex; i++)
|
|
gCanvasPalette[i] = RGB_BLACK;
|
|
|
|
gCanvasPalette[maxIndex] = RGB2(15, 15, 15);
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (IS_ALPHA(*pixel))
|
|
{
|
|
*pixel = gCanvasPaletteStart;
|
|
}
|
|
else
|
|
{
|
|
u16 quantizedColor = QuantizePixel_Standard(pixel);
|
|
u8 curIndex = 1;
|
|
if (curIndex < maxIndex)
|
|
{
|
|
if (gCanvasPalette[curIndex] == RGB_BLACK)
|
|
{
|
|
// The quantized color does not match any existing color in the
|
|
// palette, so we add it to the palette.
|
|
// This if block seems pointless because the below while loop handles
|
|
// this same logic.
|
|
gCanvasPalette[curIndex] = quantizedColor;
|
|
*pixel = gCanvasPaletteStart + curIndex;
|
|
}
|
|
else
|
|
{
|
|
while (curIndex < maxIndex)
|
|
{
|
|
if (gCanvasPalette[curIndex] == RGB_BLACK)
|
|
{
|
|
// The quantized color does not match any existing color in the
|
|
// palette, so we add it to the palette.
|
|
gCanvasPalette[curIndex] = quantizedColor;
|
|
*pixel = gCanvasPaletteStart + curIndex;
|
|
break;
|
|
}
|
|
|
|
if (gCanvasPalette[curIndex] == quantizedColor)
|
|
{
|
|
// The quantized color matches this existing color in the
|
|
// palette, so we use this existing color for the pixel.
|
|
*pixel = gCanvasPaletteStart + curIndex;
|
|
break;
|
|
}
|
|
|
|
curIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (curIndex == maxIndex)
|
|
{
|
|
// The entire palette's colors are already in use, which means
|
|
// the base image has too many colors to handle. This error is handled
|
|
// by marking such pixels as gray color.
|
|
curIndex = maxIndex;
|
|
*pixel = curIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void QuantizePalette_BlackAndWhite(void)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (IS_ALPHA(*pixel))
|
|
{
|
|
*pixel = gCanvasPaletteStart;
|
|
}
|
|
else
|
|
{
|
|
if (QuantizePixel_BlackAndWhite(pixel) == RGB_BLACK)
|
|
{
|
|
// Black is the first color in the quantized palette.
|
|
*pixel = gCanvasPaletteStart + 1;
|
|
}
|
|
else
|
|
{
|
|
// White is the second color in the quantized palette.
|
|
*pixel = gCanvasPaletteStart + 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void QuantizePalette_GrayscaleSmall(void)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (IS_ALPHA(*pixel))
|
|
*pixel = gCanvasPaletteStart;
|
|
else
|
|
*pixel = QuantizePixel_GrayscaleSmall(pixel) + gCanvasPaletteStart;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void QuantizePalette_Grayscale(void)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (IS_ALPHA(*pixel))
|
|
*pixel = gCanvasPaletteStart;
|
|
else
|
|
*pixel = QuantizePixel_Grayscale(pixel) + gCanvasPaletteStart;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void QuantizePalette_PrimaryColors(void)
|
|
{
|
|
u8 i, j;
|
|
|
|
for (j = 0; j < gCanvasRowEnd; j++)
|
|
{
|
|
u16 *pixelRow = &gCanvasPixels[(gCanvasRowStart + j) * gCanvasWidth];
|
|
u16 *pixel = &pixelRow[gCanvasColumnStart];
|
|
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
|
|
{
|
|
if (IS_ALPHA(*pixel))
|
|
*pixel = gCanvasPaletteStart;
|
|
else
|
|
*pixel = QuantizePixel_PrimaryColors(pixel) + gCanvasPaletteStart;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Quantizes the pixel's color channels to nearest multiple of 4, and clamps to [6, 30].
|
|
static u16 QuantizePixel_Standard(u16 *pixel)
|
|
{
|
|
u16 red = GET_R(*pixel);
|
|
u16 green = GET_G(*pixel);
|
|
u16 blue = GET_B(*pixel);
|
|
|
|
// Quantize color channels to muliples of 4, rounding up.
|
|
if (red & 3)
|
|
red = (red & 0x1C) + 4;
|
|
if (green & 3)
|
|
green = (green & 0x1C) + 4;
|
|
if (blue & 3)
|
|
blue = (blue & 0x1C) + 4;
|
|
|
|
// Clamp channels to [6, 30].
|
|
if (red < 6)
|
|
red = 6;
|
|
if (red > 30)
|
|
red = 30;
|
|
if (green < 6)
|
|
green = 6;
|
|
if (green > 30)
|
|
green = 30;
|
|
if (blue < 6)
|
|
blue = 6;
|
|
if (blue > 30)
|
|
blue = 30;
|
|
|
|
return RGB2(red, green, blue);
|
|
}
|
|
|
|
static u16 QuantizePixel_PrimaryColors(u16 *color)
|
|
{
|
|
u16 red = GET_R(*color);
|
|
u16 green = GET_G(*color);
|
|
u16 blue = GET_B(*color);
|
|
|
|
if (red < 12 && green < 11 && blue < 11)
|
|
return 1;
|
|
|
|
if (red > 19 && green > 19 && blue > 19)
|
|
return 2;
|
|
|
|
if (red > 19)
|
|
{
|
|
if (green > 19)
|
|
{
|
|
if (blue > 14)
|
|
return 2;
|
|
else
|
|
return 7;
|
|
}
|
|
else if (blue > 19)
|
|
{
|
|
if (green > 14)
|
|
return 2;
|
|
else
|
|
return 8;
|
|
}
|
|
}
|
|
|
|
if (green > 19 && blue > 19)
|
|
{
|
|
if (red > 14)
|
|
return 2;
|
|
else
|
|
return 9;
|
|
}
|
|
|
|
if (red > 19)
|
|
{
|
|
if (green > 11)
|
|
{
|
|
if (blue > 11)
|
|
{
|
|
if (green < blue)
|
|
return 8;
|
|
else
|
|
return 7;
|
|
}
|
|
else
|
|
{
|
|
return 10;
|
|
}
|
|
}
|
|
else if (blue > 11)
|
|
{
|
|
return 13;
|
|
}
|
|
else
|
|
{
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
if (green > 19)
|
|
{
|
|
if (red > 11)
|
|
{
|
|
if (blue > 11)
|
|
{
|
|
if (red < blue)
|
|
return 9;
|
|
else
|
|
return 7;
|
|
}
|
|
else
|
|
{
|
|
return 11;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (blue > 11)
|
|
return 14;
|
|
else
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
if (blue > 19)
|
|
{
|
|
if (red > 11)
|
|
{
|
|
if (green > 11)
|
|
{
|
|
if (red < green)
|
|
return 9;
|
|
else
|
|
return 8;
|
|
}
|
|
}
|
|
else if (green > 11)
|
|
{
|
|
return 12;
|
|
}
|
|
|
|
if (blue > 11)
|
|
return 15;
|
|
else
|
|
return 6;
|
|
}
|
|
|
|
return 3;
|
|
}
|
|
|
|
static u16 QuantizePixel_GrayscaleSmall(u16 *color)
|
|
{
|
|
u16 red = GET_R(*color);
|
|
u16 green = GET_G(*color);
|
|
u16 blue = GET_B(*color);
|
|
u16 average = ((red + green + blue) / 3) & 0x1E;
|
|
if (average == 0)
|
|
return 1;
|
|
else
|
|
return average / 2;
|
|
}
|
|
|
|
static u16 QuantizePixel_Grayscale(u16 *color)
|
|
{
|
|
u16 red = GET_R(*color);
|
|
u16 green = GET_G(*color);
|
|
u16 blue = GET_B(*color);
|
|
u16 average = (red + green + blue) / 3;
|
|
return average + 1;
|
|
}
|