mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-11-18 04:27:38 +01:00
3264cf697f
Previously, aggressive loop optimizations with a new compiler were not possible due to undefined behaviour at end of arrays. A macro "UBFIX" is added to allow ifdefs for fixes which resolve undefined behavior. For example newer GCC versions will detect various bugs in the original game code and will otherwise not compile with -Werror.
1227 lines
33 KiB
C
1227 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*);
|
|
|
|
extern const u8 gPointillismPoints[][3];
|
|
|
|
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 (!(0x8000 & *pixel))
|
|
{
|
|
// Gets the grayscale value, based on the pixel's red channel.
|
|
// Also adds a delta to skew lighter or darker.
|
|
u8 grayValue = (31 & *pixel);
|
|
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 (!(0x8000 & *pixel))
|
|
{
|
|
u8 grayValue = (31 & *pixel);
|
|
if (grayValue > 31 - highlight)
|
|
grayValue = 31 - (highlight >> 1);
|
|
|
|
*pixel = RGB2(grayValue, grayValue, grayValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ApplyImageEffect_Pointillism(void)
|
|
{
|
|
u32 i;
|
|
for (i = 0; i < 3200; 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 (!(0x8000 & *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 (!(0x8000 & *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 (!(0x8000 & *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 (!(0x8000 & *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 (!(0x8000 & *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 < 64; i++)
|
|
{
|
|
for (j = 0; j < 64; j++, pixel++)
|
|
{
|
|
if (!(0x8000 & *pixel))
|
|
*pixel = QuantizePixel_Invert(pixel);
|
|
}
|
|
}
|
|
|
|
// Blur the pixels twice.
|
|
for (j = 0; j < 64; j++)
|
|
{
|
|
pixel = &gCanvasPixels[j];
|
|
prevPixel = *pixel;
|
|
*pixel = 0x8000;
|
|
for (i = 1, pixel += 64; i < 63; i++, pixel += 64)
|
|
{
|
|
if (!(0x8000 & *pixel))
|
|
{
|
|
*pixel = QuantizePixel_BlurHard(&prevPixel, pixel, pixel + 64);
|
|
prevPixel = *pixel;
|
|
}
|
|
}
|
|
|
|
*pixel = 0x8000;
|
|
pixel = &gCanvasPixels[j];
|
|
prevPixel = *pixel;
|
|
*pixel = 0x8000;
|
|
for (i = 1, pixel += 64; i < 63; i++, pixel += 64)
|
|
{
|
|
if (!(0x8000 & *pixel))
|
|
{
|
|
*pixel = QuantizePixel_BlurHard(&prevPixel, pixel, pixel + 64);
|
|
prevPixel = *pixel;
|
|
}
|
|
}
|
|
|
|
*pixel = 0x8000;
|
|
}
|
|
|
|
// 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 < 64; i++)
|
|
{
|
|
for (j = 0; j < 64; j++, pixel++)
|
|
{
|
|
if (!(0x8000 & *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 (!(0x8000 & *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 (!(0x8000 & *pixel))
|
|
{
|
|
*pixel = QuantizePixel_MotionBlur(&prevPixel, pixel);
|
|
prevPixel = *pixel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct PointillismPoint
|
|
{
|
|
u8 column;
|
|
u8 row;
|
|
u16 delta;
|
|
};
|
|
|
|
static void AddPointillismPoints(u16 arg0)
|
|
{
|
|
u8 i;
|
|
bool8 offsetDownLeft;
|
|
u8 colorType;
|
|
struct PointillismPoint points[6];
|
|
|
|
points[0].column = gPointillismPoints[arg0][0];
|
|
points[0].row = gPointillismPoints[arg0][1];
|
|
points[0].delta = (gPointillismPoints[arg0][2] >> 3) & 7;
|
|
|
|
colorType = (gPointillismPoints[arg0][2] >> 1) & 3;
|
|
offsetDownLeft = gPointillismPoints[arg0][2] & 1;
|
|
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 > 63 || points[i].row > 63)
|
|
{
|
|
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 * 64] + points[i].column;
|
|
|
|
if (!(0x8000 & *pixel))
|
|
{
|
|
u16 red = (*pixel) & 0x1F;
|
|
u16 green = (*pixel >> 5) & 0x1F;
|
|
u16 blue = (*pixel >> 10) & 0x1F;
|
|
|
|
switch (colorType)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
switch (((gPointillismPoints[arg0][2] >> 3) & 7) % 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 = clr & 0x1F;
|
|
s32 g = (clr >> 5) & 0x1F;
|
|
s32 b = (clr >> 10) & 0x1F;
|
|
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 = *color & 0x1F;
|
|
u16 green = (*color >> 5) & 0x1F;
|
|
u16 blue = (*color >> 10) & 0x1F;
|
|
|
|
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 = *color & 0x1F;
|
|
u16 green = (*color >> 5) & 0x1F;
|
|
u16 blue = (*color >> 10) & 0x1F;
|
|
|
|
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 (*pixelA & 0x8000)
|
|
return 0x8000;
|
|
if (*pixelB & 0x8000)
|
|
return RGB_BLACK;
|
|
|
|
return *pixelA;
|
|
}
|
|
|
|
return RGB_BLACK;
|
|
}
|
|
|
|
static u16 QuantizePixel_Invert(u16 *color)
|
|
{
|
|
u16 red = *color & 0x1F;
|
|
u16 green = (*color >> 5) & 0x1F;
|
|
u16 blue = (*color >> 10) & 0x1F;
|
|
|
|
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] = (*prevPixel >> 0) & 0x1F;
|
|
pixelChannels[0][1] = (*prevPixel >> 5) & 0x1F;
|
|
pixelChannels[0][2] = (*prevPixel >> 10) & 0x1F;
|
|
pixelChannels[1][0] = (*curPixel >> 0) & 0x1F;
|
|
pixelChannels[1][1] = (*curPixel >> 5) & 0x1F;
|
|
pixelChannels[1][2] = (*curPixel >> 10) & 0x1F;
|
|
|
|
// 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 = (*curPixel >> 0) & 0x1F;
|
|
green = (*curPixel >> 5) & 0x1F;
|
|
blue = (*curPixel >> 10) & 0x1F;
|
|
|
|
prevAvg = (((*prevPixel >> 0) & 0x1F) + ((*prevPixel >> 5) & 0x1F) + ((*prevPixel >> 10) & 0x1F)) / 3;
|
|
curAvg = (((*curPixel >> 0) & 0x1F) + ((*curPixel >> 5) & 0x1F) + ((*curPixel >> 10) & 0x1F)) / 3;
|
|
nextAvg = (((*nextPixel >> 0) & 0x1F) + ((*nextPixel >> 5) & 0x1F) + ((*nextPixel >> 10) & 0x1F)) / 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 = (*curPixel >> 0) & 0x1F;
|
|
green = (*curPixel >> 5) & 0x1F;
|
|
blue = (*curPixel >> 10) & 0x1F;
|
|
|
|
prevAvg = (((*prevPixel >> 0) & 0x1F) + ((*prevPixel >> 5) & 0x1F) + ((*prevPixel >> 10) & 0x1F)) / 3;
|
|
curAvg = (((*curPixel >> 0) & 0x1F) + ((*curPixel >> 5) & 0x1F) + ((*curPixel >> 10) & 0x1F)) / 3;
|
|
nextAvg = (((*nextPixel >> 0) & 0x1F) + ((*nextPixel >> 5) & 0x1F) + ((*nextPixel >> 10) & 0x1F)) / 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] << 0xC);
|
|
dest[1] = src[4] | (src[5] << 4) | (src[6] << 8) | (src[7] << 0xC);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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] = RGB2(0, 0, 0);
|
|
gCanvasPalette[1] = RGB2(6, 6, 6);
|
|
gCanvasPalette[2] = RGB2(29, 29, 29);
|
|
gCanvasPalette[3] = RGB2(11, 11, 11);
|
|
gCanvasPalette[4] = RGB2(29, 6, 6);
|
|
gCanvasPalette[5] = RGB2(6, 29, 6);
|
|
gCanvasPalette[6] = RGB2(6, 6, 29);
|
|
gCanvasPalette[7] = RGB2(29, 29, 6);
|
|
gCanvasPalette[8] = RGB2(29, 6, 29);
|
|
gCanvasPalette[9] = RGB2(6, 29, 29);
|
|
gCanvasPalette[10] = RGB2(29, 11, 6);
|
|
gCanvasPalette[11] = RGB2(11, 29, 6);
|
|
gCanvasPalette[12] = RGB2(6, 11, 29);
|
|
gCanvasPalette[13] = RGB2(29, 6, 11);
|
|
gCanvasPalette[14] = RGB2(6, 29, 11);
|
|
gCanvasPalette[15] = RGB2(11, 6, 29);
|
|
}
|
|
|
|
static void SetPresetPalette_BlackAndWhite(void)
|
|
{
|
|
gCanvasPalette[0] = RGB2(0, 0, 0);
|
|
gCanvasPalette[1] = RGB2(0, 0, 0);
|
|
gCanvasPalette[2] = RGB2(31, 31, 31);
|
|
}
|
|
|
|
static void SetPresetPalette_GrayscaleSmall(void)
|
|
{
|
|
u8 i;
|
|
|
|
gCanvasPalette[0] = RGB2(0, 0, 0);
|
|
gCanvasPalette[1] = RGB2(0, 0, 0);
|
|
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] = RGB2(0, 0, 0);
|
|
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 (*pixel & 0x8000)
|
|
{
|
|
*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 (*pixel & 0x8000)
|
|
{
|
|
*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 (*pixel & 0x8000)
|
|
*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 (*pixel & 0x8000)
|
|
*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 (*pixel & 0x8000)
|
|
*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 = *pixel & 0x1F;
|
|
u16 green = (*pixel >> 5) & 0x1F;
|
|
u16 blue = (*pixel >> 10) & 0x1F;
|
|
|
|
// 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 = *color & 0x1F;
|
|
u16 green = (*color >> 5) & 0x1F;
|
|
u16 blue = (*color >> 10) & 0x1F;
|
|
|
|
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 = *color & 0x1F;
|
|
u16 green = (*color >> 5) & 0x1F;
|
|
u16 blue = (*color >> 10) & 0x1F;
|
|
u16 average = ((red + green + blue) / 3) & 0x1E;
|
|
if (average == 0)
|
|
return 1;
|
|
else
|
|
return average / 2;
|
|
}
|
|
|
|
static u16 QuantizePixel_Grayscale(u16 *color)
|
|
{
|
|
u16 red = *color & 0x1F;
|
|
u16 green = (*color >> 5) & 0x1F;
|
|
u16 blue = (*color >> 10) & 0x1F;
|
|
u16 average = (red + green + blue) / 3;
|
|
return average + 1;
|
|
}
|