Downgoat-ify Animals!

25

4

My Downgoat avatar has several distinct features

These features can however also apply to any other animal.

Specification

Given an image of an animal (technically can be anything), you must apply the Downgoat properties to it.

Border pixels are the outer most pixels of the image.

The background color is defined as the most common color in the border pixels. When that color is interpreted as an (r, g, b, a) color, with each channel from [0, 256), the background is defined as the area where the every channel of the given pixels' is within 50 of the "background color".

The process:

  • Setting the background and all transparent pixels to #232323, RGBA(35, 35, 35, 1.00)
  • Set all pixels which aren't the background to #FC0D1B, RGBA(252, 13, 27, 1.00).
  • Vertically reflect the image
  • Add the text -1 in Arial font, in the center of the image. The total height of the text should be 25% of the image's smallest dimension (i.e. min(width, height) * 0.25).

Rules

  • Feel free to assume the fonts are already installed
  • Your threshold is supposed to be 50, however you can take this number as input too. You do not get any decreased byte count anything for doing this however
  • Refer to our image I/O defaults for any questions related to I/O formats.
  • Image will always be at least 3x3 in size
  • If there is no most common "border pixel color", it doesn't matter which border pixel color you use as the "background color", this however must be consistent.

Examples

Input: Goat

Output: Downgoat


Input: Squid

Output: Downsquid

Input: Downgoat

Output: Downdowngoat


Input: Dennis

Output: Downdennis


Input: Trichoplax

Output: Downtrichoplax


More examples?

An online implementation is available here: vihan.org/p/downgoatify/#50


This is so shortest code in bytes wins. Good luck!

Downgoat

Posted 2016-11-14T00:39:33.207

Reputation: 27 116

6-1 First test case not an animal -_- – Geobits – 2016-11-14T01:00:14.107

10-1ˢᵗ test case not an animal either. – Adám – 2016-11-14T01:16:48.530

@Geobits okay, fine fixed – Downgoat – 2016-11-14T03:50:31.023

8Not a fan of demon dennis – downrep_nation – 2016-11-14T07:41:36.327

1You could eliminate the worry about whether it's an animal by changing "animal" to "avatar" in the title and throughout the question. – Glenn Randers-Pehrson – 2016-11-14T12:57:44.647

4What is the point of enforcing the font to use? The only thing this does is penalize languages where changing font takes more bytes – Fatalize – 2016-11-14T14:38:00.900

1@Fatalize because image input can be taken as an array of channels, I want this to be more complex than some basic array transformation and drawing a vertical line in the middle and calling that a one – Downgoat – 2016-11-14T14:40:40.397

@Downgoat You have removed the penguin image. Does this mean that the program does not need to work with image formats having an "alpha" channel? – Martin Rosenau – 2016-11-15T07:10:51.317

The background definition makes this way harder than necessary. You could also have said to provide a B/W image, or transparent/non-transparent. I thought about heading for a CSS3 solution, but like that it's simply impossible. – Cedric Reichenbach – 2016-11-15T12:27:56.537

I suspect that your example "output" for the dennis-the-menace image was generated from a different (better, without JPEG artifacts) original. I was not able to avoid getting JPEG artifacts in my result. – Glenn Randers-Pehrson – 2016-11-19T23:07:58.133

@GlennRanders-Pehrson you shouldn't have a problem with the fuzziness if the threshold is implemented correctly. Perhaps check that? – Downgoat – 2016-11-19T23:10:13.717

Well, "fuzz 8%" works better but still not an exact repro of your result. It appears that in that case my script was using 50/65k rather than 50/255 as the threshold. – Glenn Randers-Pehrson – 2016-11-20T00:16:14.163

Answers

9

ImageMagick 7.0.3 + bash + sed, 379 bytes

M=magick K=\#232323 P='-chop x%[fx:u[0].h-2]-0+1'
$M $1 -depth 8 ppm:W;$M W $P T;$M W -rotate 90 -shave 1x $P L
C=`$M T L +append -statistic mode +0 txt:-|sed -e "1d;s/.*#/#/;s/ .*//;q"`
$M W -background $K -flatten -fill $K -fuzz 20% -opaque $C +fuzz -fill \#FC0D1B +opaque $K -flip -pointsize %[fx:.282*min\(u[0].w,u[0].h\)] -fill white -draw 'gravity center text 0,0 "-1"' x:

Ungolfed

# Make aliases for things we'll use repeatedly
M=magick K=\#232323 P='-chop x%[fx:u[0].h-2]-0+1'

# Copy the input file to a PPM file
$M $1 -depth 8 ppm:W

# Extract the border pixels into "T" (top and bottom), and "L" (left and right)
# PPM files, removing the ends from the "L" because they were already counted 
$M W $P T;$M W -rotate 90 -shave 1x $P L

# Put the borders into one strip and reduce to a txt image of the "mode"
# color on stdout, then convert the first pixel to hex format string "C"
C=`$M T L +append -statistic mode +0 txt:-|sed -e "1d;s/.*#/#/;s/ .*//;q"`

# Make background "#232323" and compose transparent pixels against it
# Convert any pixels with color within "fuzz" distance of background to #232323
# Convert any remaining pixels to "#FC0D1B"
# Flip the image vertically
# Determine pointsize for text, 1/5 of min(w,h). 1 pixel == 1.44 points
# Draw text "-1" centered on the image
# Display the result to an X window

$M W -background $K -flatten                     \
-fill $K -fuzz 20% -opaque $C                    \
+fuzz -fill \#FC0D1B +opaque $K                  \
-flip                                            \
-pointsize %[fx:.282*min\(u[0].w,u[0].h\)]       \
-fill white -draw 'gravity center text 0,0 "-1"' \
x:

Inputs and Outputs

I'm getting a quite different answer for the dennis image, probably because ImageMagick's "-fuzz" computes a sphere with diameter of 2N units in rgb coordinates while the rules call for computing a cube with sides of 101 units in rgb coordinates. Varying the "fuzz" helped some. Also, the JPEG artifacts in the original seem to be interfering with the conversion.

Glenn Randers-Pehrson

Posted 2016-11-14T00:39:33.207

Reputation: 1 877

I'm not familliar with imageMagick, so this may be very uneducated, but can you replace the #0000 color code with simply #0? – tuskiomi – 2016-11-16T20:15:17.933

No, #0 doesn't work. Neither does #000 because that's opaque black and we need transparent black here. – Glenn Randers-Pehrson – 2016-11-16T20:22:15.093

The bug that I mentioned, # ImageMagick 7.0.1 through 7.0.3-7 fails here has been reported to the IM developers and is fixed in version 7.0.3-8. – Glenn Randers-Pehrson – 2016-11-20T16:52:23.293

10

C, 32-bit Windows, 987 bytes

#include <windows.h>
#define A CreateCompatibleDC(c)
#define F z=GetPixel(d,x,y);r=z;g=z>>8;b=z>>16
#define C(X,Y) (X<0||Y<0||X>=s[2]||Y>=s[3]||!GetPixel(e,X,Y))
#define D ((C(x-1,y)||C(x+1,y)||C(x,y-1)||C(x,y+1))&&!C(x,y))
#define E(X,Y) ((Z+X-Y)*(Z+X-Y)<2501)
main(int a,int*n){HDC c,d,e,f,w;int x,y,s[9],z,*o,m,t,Z;unsigned char r,g,b,R,G,B;c=GetDC(w=GetDesktopWindow());d=A;e=A;SelectObject(d,f=LoadImage(0,n[1],0,0,0,16));SelectObject(e,LoadImage(0,n[2],0,0,0,16));GetObject(f,24,s+1);o=LocalAlloc(64/*Fixed,ZeroInit*/,8*s[2]*s[3]);for(x=t=Z=s[1]=s[0]=0;x<s[2];x++)for(y=0;y<s[3];y++)if D{F;for(m=0;m<t&&o[m]!=z;m+=2);o[m]=z;o[m+1]++;t+=2*(m>=t);}for(x=y=1;x<t;x+=2)if(o[x]>o[y])y=x;z=o[y-1];R=z;G=z>>8;B=z>>16;for(x=0;x<s[2];x++)for(y=0;y<s[3];y++){F;SetPixel(c,x,s[3]-y-1,(C(x,y)||(E(r,R)&&E(g,G)&&E(b,B)))?0x232323:0x1B0DFC);}SelectObject(c,CreateFont(-(s[2]>>2),0,0,0,400,0,0,0,0,0,0,0,0,"Arial"));SetBkMode(c,1);SetTextColor(c,0xFFFFFF);DrawText(c,"-1",2,s,37);ReleaseDC(w,c);}
  • The file is saved with LF as line end, not with CR+LF to save some bytes.
  • The program is written in a way that compiler warnings are generated to save some more bytes.
  • The file will probably not compile as 64-bit program because the array "s[]" is used to do some implicit castings...
  • The program takes two images (file names are given via command line):
    • The first image (first command line argument) is the R/G/B image in Windows .BMP format
    • The second image is the Alpha channel (black means: 0%, any other colour means: not 0%); the file is also in .BMP format and must have the same size or be larger than the first image
  • The output image is displayed in the left upper corner of the screen
  • I couldn't reproduce the boy with the yellow hair. Yellow seems to be too far away (> 50) from white!

Ungolfed version:

#include <windows.h>

/*
 * Although this line costs us 32 bytes
 * Replacing "CreateCompatibleDC(c)" we'll
 * save 42 bytes in the golfed version later
 * so we save 10 bytes using this define!
 */
#define A CreateCompatibleDC(c)

/*
 * Macro: Get a pixel value at (x,y) to z
 * Also get r, g, b
 */
#define F z=GetPixel(d,x,y); \
    r=z; \
    g=z>>8; \
    b=z>>16

/*
 * Macro checking if a pixel is a
 * transparent colour or lies outside the
 * image
 */
#define C(X,Y) (X<0 || Y<0 || X>=s[2] || Y>=s[3] || !GetPixel(e,X,Y))

/*
 * Macro checking if a pixel at (x,y) is a border pixel
 */
#define D ((C(x-1,y) || C(x+1,y) || C(x,y-1) || C(x,y+1)) && !C(x,y))

/*
 * Macro checking if the difference between X and Y is less than 50
 * The variable "Z" must be type "int" and zero. It is used to
 * perform an implicit cast from "BYTE" to "int".
 */
#define E(X,Y) ((Z+X-Y)*(Z+X-Y)<2501)

/*
 * Note that the second argument is "char **",
 * not "int *".
 * We ignore resulting compiler warnings...
 */
main(int a, int * n)
{
    HDC c, d, e, f, w;
    int x, y, s[9], z, *o, m, t, Z;
    unsigned char r, g, b, R, G, B;

    /*
     * Get the HDC handle to the
     * screen (allowing us to create HDCs
     * for accessing bitmap files as well as
     * drawing directly to the screen!)
     */
    c=GetDC(w=GetDesktopWindow());
    /*
     * Create two virtual HDCs for accessing
     * the bitmap files.
     */
    d=A; /* Macro */
    e=A; /* Macro */
    /*
     * Load the two images:
     * The first argument is the image file with
     * the R/G/B channel
     * The second argument is the image file
     * containing the mask defined by the Alpha
     * channel:
     *   Black means = Alpha=0
     *   White means = Alpha>0
     *   (Any other colour means: Alpha>0)
     *
     * Note that "f" is of the type "HBITMAP",
     * not "HDC". We save 4 bytes in the golfed
     * version using HDC instead of HBITMAP and
     * compile the C file with compiler warnings
     * switched off!
     *
     * The second image should have the same size
     * as the first one. However it may be larger
     * than the first one. It must not be smaller!
     */
    SelectObject(d,f=LoadImage(0,n[1],0,0,0,16 /* 16=LR_LOADFROMFILE */));
    SelectObject(e,LoadImage(0,n[2],0,0,0,16));
    /*
     * Get the image information (size)
     */
    GetObject(f,24,s+1);
    /*
     * Search all background colours
     */
    o=LocalAlloc(64 /* Fixed, ZeroInit */,8*s[2]*s[3]);
    for(x=t=Z=s[1]=s[0]=0;x<s[2];x++)
        for(y=0;y<s[3];y++)
            if D
    {
        F; /* Macro */
        for(m=0;m<t && o[m]!=z;m+=2);
        o[m]=z;
        o[m+1]++;
        t+=2*(m>=t);
    }
    /*
     * Search the most common one
     */
    for(x=y=1;x<t;x+=2) if(o[x]>o[y]) y=x;
    z=o[y-1];
    R=z;
    G=z>>8;
    B=z>>16;
    /*
     * Draw the image directly to the screen
     */
    for(x=0;x<s[2];x++)
        for(y=0;y<s[3];y++)
    {
        F; /* Macro */
        /* C and E are macros: */
        SetPixel(c,x,s[3]-y-1,(C(x,y) ||
            (E(r,R) && E(g,G) && E(b,B)))?
            0x232323:0x1B0DFC);
    }
    /*
     * Draw the text over the image
     */
    SelectObject(c,CreateFont(-(s[2]>>2),0,0,0,400,0,0,0,0,0,0,0,0,"Arial"));
    SetBkMode(c,1 /* TRANSPARENT */);
    SetTextColor(c,0xFFFFFF);
    DrawText(c,"-1",2,s,37 /* center, vcenter, singleline */);
    /*
     * Unfortunately DrawText() won't work
     * when not doing this!
     */
    ReleaseDC(w,c);
}

Martin Rosenau

Posted 2016-11-14T00:39:33.207

Reputation: 1 921