pokeemerald/src/contest_painting_effects.c

1385 lines
36 KiB
C
Raw Normal View History

2018-12-29 11:50:00 +01:00
#include "global.h"
#include "contest_painting_effects.h"
#include "contest_painting.h"
#include "constants/rgb.h"
2019-02-02 12:44:00 +01:00
// IWRAM common
u8 gCanvasColumnStart;
u16 (*gCanvasPixels)[][32];
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 ContestPaintingContext *context)
2018-12-29 11:50:00 +01:00
{
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)
2018-12-29 11:50:00 +01:00
{
case IMAGE_EFFECT_POINTILLISM:
ApplyImageEffect_Pointillism();
2018-12-29 11:50:00 +01:00
break;
case IMAGE_EFFECT_BLUR:
ApplyImageEffect_Blur();
2018-12-29 11:50:00 +01:00
break;
case IMAGE_EFFECT_OUTLINE_COLORED:
ApplyImageEffect_BlackOutline();
ApplyImageEffect_PersonalityColor(gCanvasMonPersonality);
2018-12-29 11:50:00 +01:00
break;
case IMAGE_EFFECT_INVERT_BLACK_WHITE:
ApplyImageEffect_BlackOutline();
ApplyImageEffect_Invert();
ApplyImageEffect_BlackAndWhite();
case IMAGE_EFFECT_INVERT:
ApplyImageEffect_Invert();
2018-12-29 11:50:00 +01:00
break;
case IMAGE_EFFECT_THICK_BLACK_WHITE:
ApplyImageEffect_BlackOutline();
ApplyImageEffect_BlurRight();
ApplyImageEffect_BlurRight();
ApplyImageEffect_BlurDown();
ApplyImageEffect_BlackAndWhite();
2018-12-29 11:50:00 +01:00
break;
case IMAGE_EFFECT_SHIMMER:
ApplyImageEffect_Shimmer();
2018-12-29 11:50:00 +01:00
break;
case IMAGE_EFFECT_OUTLINE:
ApplyImageEffect_BlackOutline();
2018-12-29 11:50:00 +01:00
break;
case IMAGE_EFFECT_BLUR_RIGHT:
ApplyImageEffect_BlurRight();
2018-12-29 11:50:00 +01:00
break;
case IMAGE_EFFECT_BLUR_DOWN:
ApplyImageEffect_BlurDown();
2018-12-29 11:50:00 +01:00
break;
case IMAGE_EFFECT_GRAYSCALE_LIGHT:
ApplyImageEffect_Grayscale();
ApplyImageEffect_RedChannelGrayscale(3);
2018-12-29 11:50:00 +01:00
break;
case IMAGE_EFFECT_CHARCOAL:
ApplyImageEffect_BlackOutline();
ApplyImageEffect_BlurRight();
ApplyImageEffect_BlurDown();
ApplyImageEffect_BlackAndWhite();
ApplyImageEffect_Blur();
ApplyImageEffect_Blur();
ApplyImageEffect_RedChannelGrayscale(2);
ApplyImageEffect_RedChannelGrayscaleHighlight(4);
2018-12-29 11:50:00 +01:00
break;
}
}
static void ApplyImageEffect_RedChannelGrayscale(u8 delta)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
for (j = 0; j < gCanvasRowEnd; j++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][(gCanvasRowStart + j) * gCanvasWidth];
u16 *pixel = &pixelRow[gCanvasColumnStart];
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
2018-12-29 11:50:00 +01:00
{
// 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);
2018-12-29 11:50:00 +01:00
}
}
}
}
static void ApplyImageEffect_RedChannelGrayscaleHighlight(u8 highlight)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
for (j = 0; j < gCanvasRowEnd; j++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][(gCanvasRowStart + j) * gCanvasWidth];
u16 *pixel = &pixelRow[gCanvasColumnStart];
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
2018-12-29 11:50:00 +01:00
{
u8 grayValue = (31 & *pixel);
if (grayValue > 31 - highlight)
grayValue = 31 - (highlight >> 1);
2018-12-29 11:50:00 +01:00
*pixel = RGB2(grayValue, grayValue, grayValue);
2018-12-29 11:50:00 +01:00
}
}
}
}
static void ApplyImageEffect_Pointillism(void)
2018-12-29 11:50:00 +01:00
{
u32 i;
for (i = 0; i < 3200; i++)
AddPointillismPoints(i);
2018-12-29 11:50:00 +01:00
}
static void ApplyImageEffect_Grayscale(void)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
for (j = 0; j < gCanvasRowEnd; j++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][(gCanvasRowStart + j) * gCanvasWidth];
u16 *pixel = &pixelRow[gCanvasColumnStart];
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
*pixel = ConvertColorToGrayscale(pixel);
2018-12-29 11:50:00 +01:00
}
}
}
static void ApplyImageEffect_Blur(void)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
for (i = 0; i < gCanvasColumnEnd; i++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][gCanvasRowStart * gCanvasWidth];
u16 *pixel = &pixelRow[gCanvasColumnStart + i];
u16 prevPixel = *pixel;
2018-12-29 11:50:00 +01:00
j = 1;
pixel += gCanvasWidth;
while (j < gCanvasRowEnd - 1)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
2018-12-29 11:50:00 +01:00
{
*pixel = QuantizePixel_Blur(&prevPixel, pixel, pixel + gCanvasWidth);
prevPixel = *pixel;
2018-12-29 11:50:00 +01:00
}
j++;
pixel += gCanvasWidth;
2018-12-29 11:50:00 +01:00
}
}
}
static void ApplyImageEffect_PersonalityColor(u8 personality)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
for (j = 0; j < gCanvasRowEnd; j++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][(gCanvasRowStart + j) * gCanvasWidth];
u16 *pixel = &pixelRow[gCanvasColumnStart];
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
*pixel = QuantizePixel_PersonalityColor(pixel, personality);
2018-12-29 11:50:00 +01:00
}
}
}
static void ApplyImageEffect_BlackAndWhite(void)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
for (j = 0; j < gCanvasRowEnd; j++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][(gCanvasRowStart + j) * gCanvasWidth];
u16 *pixel = &pixelRow[gCanvasColumnStart];
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
*pixel = QuantizePixel_BlackAndWhite(pixel);
2018-12-29 11:50:00 +01:00
}
}
}
static void ApplyImageEffect_BlackOutline(void)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
u16 *pixel;
2018-12-29 11:50:00 +01:00
// Handle top row of pixels first.
for (j = 0; j < gCanvasRowEnd; j++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][(gCanvasRowStart + j) * gCanvasWidth];
pixel = &pixelRow[gCanvasColumnStart];
*pixel = QuantizePixel_BlackOutline(pixel, pixel + 1);
for (i = 1, pixel++; i < gCanvasColumnEnd - 1; i++, pixel++)
2018-12-29 11:50:00 +01:00
{
*pixel = QuantizePixel_BlackOutline(pixel, pixel + 1);
*pixel = QuantizePixel_BlackOutline(pixel, pixel - 1);
2018-12-29 11:50:00 +01:00
}
*pixel = QuantizePixel_BlackOutline(pixel, pixel - 1);
2018-12-29 11:50:00 +01:00
}
// Handle each column from left to right.
for (i = 0; i < gCanvasColumnEnd; i++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][gCanvasRowStart * gCanvasWidth];
pixel = &pixelRow[gCanvasColumnStart + i];
*pixel = QuantizePixel_BlackOutline(pixel, pixel + gCanvasWidth);
for (j = 1, pixel += gCanvasWidth; j < gCanvasRowEnd - 1; j++, pixel += gCanvasWidth)
2018-12-29 11:50:00 +01:00
{
*pixel = QuantizePixel_BlackOutline(pixel, pixel + gCanvasWidth);
*pixel = QuantizePixel_BlackOutline(pixel, pixel - gCanvasWidth);
2018-12-29 11:50:00 +01:00
}
*pixel = QuantizePixel_BlackOutline(pixel, pixel - gCanvasWidth);
2018-12-29 11:50:00 +01:00
}
}
static void ApplyImageEffect_Invert(void)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
for (j = 0; j < gCanvasRowEnd; j++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][(gCanvasRowStart + j) * gCanvasWidth];
u16 *pixel = &pixelRow[gCanvasColumnStart];
for (i = 0; i < gCanvasColumnEnd; i++, pixel++)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
*pixel = QuantizePixel_Invert(pixel);
2018-12-29 11:50:00 +01:00
}
}
}
static void ApplyImageEffect_Shimmer(void)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
u16 *pixel;
u16 prevPixel;
2018-12-29 11:50:00 +01:00
// First, invert all of the colors.
pixel = (*gCanvasPixels)[0];
2018-12-29 11:50:00 +01:00
for (i = 0; i < 64; i++)
{
for (j = 0; j < 64; j++, pixel++)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
*pixel = QuantizePixel_Invert(pixel);
2018-12-29 11:50:00 +01:00
}
}
// Blur the pixels twice.
2018-12-29 11:50:00 +01:00
for (j = 0; j < 64; j++)
{
pixel = &(*gCanvasPixels)[0][j];
prevPixel = *pixel;
*pixel = 0x8000;
for (i = 1, pixel += 64; i < 63; i++, pixel += 64)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
2018-12-29 11:50:00 +01:00
{
*pixel = QuantizePixel_BlurHard(&prevPixel, pixel, pixel + 64);
prevPixel = *pixel;
2018-12-29 11:50:00 +01:00
}
}
*pixel = 0x8000;
pixel = &(*gCanvasPixels)[0][j];
prevPixel = *pixel;
*pixel = 0x8000;
for (i = 1, pixel += 64; i < 63; i++, pixel += 64)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
2018-12-29 11:50:00 +01:00
{
*pixel = QuantizePixel_BlurHard(&prevPixel, pixel, pixel + 64);
prevPixel = *pixel;
2018-12-29 11:50:00 +01:00
}
}
*pixel = 0x8000;
2018-12-29 11:50:00 +01:00
}
// 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)[0];
2018-12-29 11:50:00 +01:00
for (i = 0; i < 64; i++)
{
for (j = 0; j < 64; j++, pixel++)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
*pixel = QuantizePixel_Invert(pixel);
2018-12-29 11:50:00 +01:00
}
}
}
static void ApplyImageEffect_BlurRight(void)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
for (j = 0; j < gCanvasRowEnd; j++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][(gCanvasRowStart + j) * gCanvasWidth];
u16 *pixel = &pixelRow[gCanvasColumnStart];
u16 prevPixel = *pixel;
for (i = 1, pixel++; i < gCanvasColumnEnd - 1; i++, pixel++)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
2018-12-29 11:50:00 +01:00
{
*pixel = QuantizePixel_MotionBlur(&prevPixel, pixel);
prevPixel = *pixel;
2018-12-29 11:50:00 +01:00
}
}
}
}
static void ApplyImageEffect_BlurDown(void)
2018-12-29 11:50:00 +01:00
{
u8 i, j;
for (i = 0; i < gCanvasColumnEnd; i++)
2018-12-29 11:50:00 +01:00
{
u16 *pixelRow = &(*gCanvasPixels)[0][gCanvasRowStart * gCanvasWidth];
u16 *pixel = &pixelRow[gCanvasColumnStart + i];
u16 prevPixel = *pixel;
for (j = 1, pixel += gCanvasWidth; j < gCanvasRowEnd - 1; j++, pixel += gCanvasWidth)
2018-12-29 11:50:00 +01:00
{
if (!(0x8000 & *pixel))
2018-12-29 11:50:00 +01:00
{
*pixel = QuantizePixel_MotionBlur(&prevPixel, pixel);
prevPixel = *pixel;
2018-12-29 11:50:00 +01:00
}
}
}
}
struct PointillismPoint
{
u8 column;
u8 row;
u16 delta;
};
static void AddPointillismPoints(u16 arg0)
2018-12-29 11:50:00 +01:00
{
u8 i;
bool8 offsetDownLeft;
u8 colorType;
struct PointillismPoint points[6];
2018-12-29 11:50:00 +01:00
points[0].column = gPointillismPoints[arg0][0];
points[0].row = gPointillismPoints[arg0][1];
points[0].delta = (gPointillismPoints[arg0][2] >> 3) & 7;
2018-12-29 11:50:00 +01:00
colorType = (gPointillismPoints[arg0][2] >> 1) & 3;
offsetDownLeft = gPointillismPoints[arg0][2] & 1;
for (i = 1; i < points[0].delta; i++)
2018-12-29 11:50:00 +01:00
{
if (!offsetDownLeft)
2018-12-29 11:50:00 +01:00
{
points[i].column = points[0].column - i;
points[i].row = points[0].row + i;
2018-12-29 11:50:00 +01:00
}
else
{
points[i].column = points[0].column + 1;
points[i].row = points[0].row - 1;
2018-12-29 11:50:00 +01:00
}
if (points[i].column > 63 || points[i].row > 63)
2018-12-29 11:50:00 +01:00
{
points[0].delta = i - 1;
2018-12-29 11:50:00 +01:00
break;
}
points[i].delta = points[0].delta - i;
2018-12-29 11:50:00 +01:00
}
for (i = 0; i < points[0].delta; i++)
2018-12-29 11:50:00 +01:00
{
u16 *pixel = &(*gCanvasPixels)[points[i].row * 2][points[i].column];
2018-12-29 11:50:00 +01:00
if (!(0x8000 & *pixel))
2018-12-29 11:50:00 +01:00
{
u16 red = (*pixel) & 0x1F;
u16 green = (*pixel >> 5) & 0x1F;
u16 blue = (*pixel >> 10) & 0x1F;
2018-12-29 11:50:00 +01:00
switch (colorType)
2018-12-29 11:50:00 +01:00
{
case 0:
case 1:
switch (((gPointillismPoints[arg0][2] >> 3) & 7) % 3)
2018-12-29 11:50:00 +01:00
{
case 0:
if (red >= points[i].delta)
red -= points[i].delta;
2018-12-29 11:50:00 +01:00
else
red = 0;
2018-12-29 11:50:00 +01:00
break;
case 1:
if (green >= points[i].delta)
green -= points[i].delta;
2018-12-29 11:50:00 +01:00
else
green = 0;
2018-12-29 11:50:00 +01:00
break;
case 2:
if (blue >= points[i].delta)
blue -= points[i].delta;
2018-12-29 11:50:00 +01:00
else
blue = 0;
2018-12-29 11:50:00 +01:00
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;
2018-12-29 11:50:00 +01:00
break;
}
*pixel = RGB2(red, green, blue);
2018-12-29 11:50:00 +01:00
}
}
}
static u16 ConvertColorToGrayscale(u16 *color)
2018-12-29 11:50:00 +01:00
{
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)
2018-12-29 11:50:00 +01:00
{
u16 red = *color & 0x1F;
u16 green = (*color >> 5) & 0x1F;
u16 blue = (*color >> 10) & 0x1F;
if (red < 17 && green < 17 && blue < 17)
return GetColorFromPersonality(personality);
2018-12-29 11:50:00 +01:00
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)
2018-12-29 11:50:00 +01:00
{
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)
2018-12-29 11:50:00 +01:00
{
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)
2018-12-29 11:50:00 +01:00
{
if (*pixelA != RGB_BLACK)
2018-12-29 11:50:00 +01:00
{
if (*pixelA & 0x8000)
2018-12-29 11:50:00 +01:00
return 0x8000;
if (*pixelB & 0x8000)
2018-12-29 11:50:00 +01:00
return RGB_BLACK;
return *pixelA;
2018-12-29 11:50:00 +01:00
}
return RGB_BLACK;
}
static u16 QuantizePixel_Invert(u16 *color)
2018-12-29 11:50:00 +01:00
{
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);
}
2018-12-29 12:10:06 +01:00
static u16 QuantizePixel_MotionBlur(u16 *prevPixel, u16 *curPixel)
2018-12-29 12:10:06 +01:00
{
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++)
2018-12-29 12:10:06 +01:00
{
if (pixelChannels[0][i] > pixelChannels[1][i])
diffs[i] = pixelChannels[0][i] - pixelChannels[1][i];
2018-12-29 12:10:06 +01:00
else
diffs[i] = pixelChannels[1][i] - pixelChannels[0][i];
2018-12-29 12:10:06 +01:00
}
// Find the largest diff of any of the color channels.
if (diffs[0] >= diffs[1])
2018-12-29 12:10:06 +01:00
{
if (diffs[0] >= diffs[2])
largestDiff = diffs[0];
else if (diffs[1] >= diffs[2])
largestDiff = diffs[1];
2018-12-29 12:10:06 +01:00
else
largestDiff = diffs[2];
2018-12-29 12:10:06 +01:00
}
else
{
if (diffs[1] >= diffs[2])
largestDiff = diffs[1];
else if (diffs[2] >= diffs[0])
largestDiff = diffs[2];
2018-12-29 12:10:06 +01:00
else
largestDiff = diffs[0];
2018-12-29 12:10:06 +01:00
}
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);
2018-12-29 12:10:06 +01:00
}
static u16 QuantizePixel_Blur(u16 *prevPixel, u16 *curPixel, u16 *nextPixel)
2018-12-29 12:10:06 +01:00
{
u16 red, green, blue;
u16 prevAvg, curAvg, nextAvg;
u16 prevDiff, nextDiff;
u32 diff;
2018-12-29 12:10:06 +01:00
u16 factor;
if (*prevPixel == *curPixel && *nextPixel == *curPixel)
return *curPixel;
2018-12-29 12:10:06 +01:00
red = (*curPixel >> 0) & 0x1F;
green = (*curPixel >> 5) & 0x1F;
blue = (*curPixel >> 10) & 0x1F;
2018-12-29 12:10:06 +01:00
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;
2018-12-29 12:10:06 +01:00
if (prevAvg == curAvg && nextAvg == curAvg)
return *curPixel;
2018-12-29 12:10:06 +01:00
if (prevAvg > curAvg)
prevDiff = prevAvg - curAvg;
2018-12-29 12:10:06 +01:00
else
prevDiff = curAvg - prevAvg;
2018-12-29 12:10:06 +01:00
if (nextAvg > curAvg)
nextDiff = nextAvg - curAvg;
2018-12-29 12:10:06 +01:00
else
nextDiff = curAvg - nextAvg;
2018-12-29 12:10:06 +01:00
if (prevDiff >= nextDiff)
diff = prevDiff;
2018-12-29 12:10:06 +01:00
else
diff = nextDiff;
2018-12-29 12:10:06 +01:00
factor = 31 - diff / 2;
red = (red * factor) / 31;
green = (green * factor) / 31;
blue = (blue * factor) / 31;
2018-12-29 12:10:06 +01:00
return RGB2(red, green, blue);
}
static u16 QuantizePixel_BlurHard(u16 *prevPixel, u16 *curPixel, u16 *nextPixel)
2018-12-29 12:10:06 +01:00
{
u16 red, green, blue;
u16 prevAvg, curAvg, nextAvg;
u16 prevDiff, nextDiff;
u32 diff;
2018-12-29 12:10:06 +01:00
u16 factor;
if (*prevPixel == *curPixel && *nextPixel == *curPixel)
return *curPixel;
2018-12-29 12:10:06 +01:00
red = (*curPixel >> 0) & 0x1F;
green = (*curPixel >> 5) & 0x1F;
blue = (*curPixel >> 10) & 0x1F;
2018-12-29 12:10:06 +01:00
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;
2018-12-29 12:10:06 +01:00
if (prevAvg == curAvg && nextAvg == curAvg)
return *curPixel;
2018-12-29 12:10:06 +01:00
if (prevAvg > curAvg)
prevDiff = prevAvg - curAvg;
2018-12-29 12:10:06 +01:00
else
prevDiff = curAvg - prevAvg;
2018-12-29 12:10:06 +01:00
if (nextAvg > curAvg)
nextDiff = nextAvg - curAvg;
2018-12-29 12:10:06 +01:00
else
nextDiff = curAvg - nextAvg;
2018-12-29 12:10:06 +01:00
if (prevDiff >= nextDiff)
diff = prevDiff;
2018-12-29 12:10:06 +01:00
else
diff = nextDiff;
2018-12-29 12:10:06 +01:00
factor = 31 - diff;
red = (red * factor) / 31;
green = (green * factor) / 31;
blue = (blue * factor) / 31;
2018-12-29 12:10:06 +01:00
return RGB2(red, green, blue);
}
/*
void ConvertImageProcessingToGBA(struct ContestPaintingContext *arg0)
2018-12-29 12:10:06 +01:00
{
u16 i, j, k;
u8 r5 = arg0->canvasWidth >> 3;
u8 var_24 = arg0->canvasHeight >> 3;
u16 (*var_2C)[][32] = arg0->canvasPixels;
u32 var_28 = arg0->dest;
2018-12-29 12:10:06 +01:00
if (arg0->var_16 == 2)
{
for (i = 0; i < var_24; i++)
{
for (j = 0; j < r5; j++)
{
for (k = 0; k < 8; k++)
{
(*var_2C)[][];
}
}
}
}
}
*/
NAKED
void ConvertImageProcessingToGBA(struct ContestPaintingContext *arg0)
{
asm_unified("\n\
push {r4-r7,lr}\n\
mov r7, r10\n\
mov r6, r9\n\
mov r5, r8\n\
push {r5-r7}\n\
sub sp, 0xC\n\
ldrb r1, [r0, 0x1D]\n\
lsrs r5, r1, 3\n\
ldrb r1, [r0, 0x1E]\n\
lsrs r1, 3\n\
str r1, [sp, 0x8]\n\
ldr r1, [r0, 0x4]\n\
str r1, [sp]\n\
ldr r2, [r0, 0x10]\n\
str r2, [sp, 0x4]\n\
ldrh r0, [r0, 0x16]\n\
cmp r0, 0x2\n\
bne _08126108\n\
movs r1, 0\n\
ldr r0, [sp, 0x8]\n\
cmp r1, r0\n\
bcc _08126086\n\
b _08126194\n\
_08126086:\n\
movs r0, 0\n\
adds r2, r1, 0x1\n\
mov r10, r2\n\
cmp r0, r5\n\
bcs _081260FA\n\
adds r2, r1, 0\n\
muls r2, r5\n\
mov r9, r2\n\
lsls r1, 3\n\
mov r8, r1\n\
_0812609A:\n\
movs r4, 0\n\
lsls r6, r0, 4\n\
adds r7, r0, 0x1\n\
add r0, r9\n\
lsls r0, 6\n\
ldr r1, [sp, 0x4]\n\
adds r1, r0\n\
mov r12, r1\n\
_081260AA:\n\
lsls r0, r4, 3\n\
mov r2, r12\n\
adds r3, r2, r0\n\
mov r1, r8\n\
adds r0, r1, r4\n\
lsls r0, 3\n\
muls r0, r5\n\
lsls r0, 1\n\
ldr r2, [sp]\n\
adds r0, r2, r0\n\
adds r2, r0, r6\n\
ldrh r0, [r2, 0x2]\n\
lsls r0, 8\n\
ldrh r1, [r2]\n\
orrs r0, r1\n\
strh r0, [r3]\n\
ldrh r0, [r2, 0x6]\n\
lsls r0, 8\n\
ldrh r1, [r2, 0x4]\n\
orrs r0, r1\n\
strh r0, [r3, 0x2]\n\
ldrh r0, [r2, 0xA]\n\
lsls r0, 8\n\
ldrh r1, [r2, 0x8]\n\
orrs r0, r1\n\
strh r0, [r3, 0x4]\n\
ldrh r0, [r2, 0xE]\n\
lsls r0, 8\n\
ldrh r1, [r2, 0xC]\n\
orrs r0, r1\n\
strh r0, [r3, 0x6]\n\
adds r0, r4, 0x1\n\
lsls r0, 16\n\
lsrs r4, r0, 16\n\
cmp r4, 0x7\n\
bls _081260AA\n\
lsls r0, r7, 16\n\
lsrs r0, 16\n\
cmp r0, r5\n\
bcc _0812609A\n\
_081260FA:\n\
mov r1, r10\n\
lsls r0, r1, 16\n\
lsrs r1, r0, 16\n\
ldr r2, [sp, 0x8]\n\
cmp r1, r2\n\
bcc _08126086\n\
b _08126194\n\
_08126108:\n\
movs r1, 0\n\
ldr r0, [sp, 0x8]\n\
cmp r1, r0\n\
bcs _08126194\n\
_08126110:\n\
movs r0, 0\n\
adds r2, r1, 0x1\n\
mov r10, r2\n\
cmp r0, r5\n\
bcs _08126188\n\
adds r2, r1, 0\n\
muls r2, r5\n\
mov r9, r2\n\
lsls r1, 3\n\
mov r8, r1\n\
_08126124:\n\
movs r4, 0\n\
lsls r6, r0, 4\n\
adds r7, r0, 0x1\n\
add r0, r9\n\
lsls r0, 5\n\
ldr r1, [sp, 0x4]\n\
adds r1, r0\n\
mov r12, r1\n\
_08126134:\n\
lsls r0, r4, 2\n\
mov r2, r12\n\
adds r3, r2, r0\n\
mov r1, r8\n\
adds r0, r1, r4\n\
lsls r0, 3\n\
muls r0, r5\n\
lsls r0, 1\n\
ldr r2, [sp]\n\
adds r0, r2, r0\n\
adds r2, r0, r6\n\
ldrh r1, [r2, 0x2]\n\
lsls r1, 4\n\
ldrh r0, [r2]\n\
orrs r1, r0\n\
ldrh r0, [r2, 0x4]\n\
lsls r0, 8\n\
orrs r1, r0\n\
ldrh r0, [r2, 0x6]\n\
lsls r0, 12\n\
orrs r1, r0\n\
strh r1, [r3]\n\
ldrh r1, [r2, 0xA]\n\
lsls r1, 4\n\
ldrh r0, [r2, 0x8]\n\
orrs r1, r0\n\
ldrh r0, [r2, 0xC]\n\
lsls r0, 8\n\
orrs r1, r0\n\
ldrh r0, [r2, 0xE]\n\
lsls r0, 12\n\
orrs r1, r0\n\
strh r1, [r3, 0x2]\n\
adds r0, r4, 0x1\n\
lsls r0, 16\n\
lsrs r4, r0, 16\n\
cmp r4, 0x7\n\
bls _08126134\n\
lsls r0, r7, 16\n\
lsrs r0, 16\n\
cmp r0, r5\n\
bcc _08126124\n\
_08126188:\n\
mov r1, r10\n\
lsls r0, r1, 16\n\
lsrs r1, r0, 16\n\
ldr r2, [sp, 0x8]\n\
cmp r1, r2\n\
bcc _08126110\n\
_08126194:\n\
add sp, 0xC\n\
pop {r3-r5}\n\
mov r8, r3\n\
mov r9, r4\n\
mov r10, r5\n\
pop {r4-r7}\n\
pop {r0}\n\
bx r0");
}
void ApplyImageProcessingQuantization(struct ContestPaintingContext *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)[0][(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)[0][(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)[0][(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)[0][(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)[0][(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;
}