26
\$\begingroup\$

Description

The Gameboy stores tiles as 2 bit-per-pixel 8x8 images, thus 16 bytes. Every two bytes is a complete row with all of the Low-bits of each pixel in the first byte, and all of the High-bits of each pixel in the second.

Input

Input will be exactly 16 bytes, received through Standard IO in one of the following forms:

  • Array of bytes or strings
  • 16 byte string

Per the Standard IO, these may be in a language convenient form (Deliminated string, on the stack, etc.)

Output

An image, Rendered or Saved, of the Gameboy Tile.

Scale and Aspect Ratio of each pixel is not fixed.

Each 2 bit colour of the Pallet may be anything so long as the Manhattan Distance of the RGB256 representation is atleast 48. Eg. #FFFFFF, #AAAAAA, #555555, #000000. Traditionally, although not a requirement, 00 is the lightest colour, and 11 is the darkest.

Examples

[FF, 00, 7E, FF, 85, 81, 89, 83, 93, 85, A5, 8B, C9, 97, 7E, FF]

A Pokemon Window

[7C, 7C, 00, C6, C6, 00, 00, FE, C6, C6, 00, C6, C6, 00, 00, 00]

The letter A

Final Notes

  • Standard Loopholes apply
  • An online demo, and more in-depth explanation of the implementation can be found HERE
  • This is , so fewest bytes wins! (The Gameboy only had 65536 bytes of Address Space, after all!)
  • Have Fun!
\$\endgroup\$
9
  • 2
    \$\begingroup\$ Suggested test case: 7C 7C 00 C6 C6 00 00 FE C6 C6 00 C6 C6 00 00 00 should look like a letter A as shown at the HERE link \$\endgroup\$
    – noodle man
    Feb 17 at 3:00
  • 1
    \$\begingroup\$ Could you include a worked example, please? I'm having difficulty figuring this out. \$\endgroup\$
    – Shaggy
    Feb 17 at 11:30
  • 2
    \$\begingroup\$ 65535 bytes of RAM? No way! The Gameboy's address space was 16-bit, so it could address 65536 (not 65535) bytes... but it only had 8192 bytes of main RAM (and another 8192 bytes of VRAM). Although some cartridges did contain extra. :) \$\endgroup\$
    – Keiji
    Feb 17 at 13:08
  • \$\begingroup\$ I expect an answer in Gameboy assembly language of course. \$\endgroup\$ Feb 17 at 16:30
  • 2
    \$\begingroup\$ The Standard IO Does not appear to support replacing graphical output for characters, thus this is against the challenge. An acceptable alternative is a Matrix/array of colours, which can then be replaced with the unicode characters for demonstration purposes (outside of the answer) \$\endgroup\$
    – ATaco
    Feb 17 at 20:56

24 Answers 24

26
\$\begingroup\$

Gameboy machine code, 257 bytes

00000000  f0 44 fe 90 38 fa 3e e4  e0 47 21 90 81 1a 22 13  |.D..8.>..G!...".|
00000010  1a 22 13 1a 22 13 1a 22  13 1a 22 13 1a 22 13 1a  |."..".."..".."..|
00000020  22 13 1a 22 13 1a 22 13  1a 22 13 1a 22 13 1a 22  |".."..".."..".."|
00000030  13 1a 22 13 1a 22 13 1a  22 13 1a 22 18 fe 00 00  |..".."..".."....|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000000d0  00 00 00 00 00 00 00 00  ff 00 7e ff 85 81 89 83  |..........~.....|
000000e0  93 85 a5 8b c9 97 7e ff  00 00 00 00 00 00 00 00  |......~.........|
000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000100  c7                                                |.|
00000101

Generated by this RGBASM assembly:

DEF rLY EQU $ff44
DEF rBGP EQU $ff47

SECTION "0",ROM0[0]
WaitVBlank:
    ldh a, [rLY]
    cp 144
    jr c, WaitVBlank

    ld a, %11100100    ; set background palette
    ldh [rBGP], a

    ld hl, $8190    ; override the tile data for the (R) icon
    REPT 15
        ld a, [de]
        ld [hli], a
        inc de
    ENDR
    ld a, [de]
    ld [hli], a
    jr @

SECTION "Input Data",ROM0[$d8]
DB $ff, $00, $7e, $ff, $85, $81, $89, $83, $93, $85, $a5, $8b, $c9, $97, $7e, $ff

SECTION "Entrypoint",ROM0[$100]
    rst 0    ; jump to reset vector 0

You can compile this code with rgbds: rgbasm tile.asm -o - | rgblink - -o tile.gb.

A gameboy ROM is usually at least 16K, so some emulators don't support this, but sameboy executes it with no problem. The entrypoint of the ROM, after the boot ROM executed is 0x100, so I don't think it can be any smaller.

As per the IO rules, Assembly programs may read input from some specified memory location, in this case 0xd8, which is conveniently the value of the de register when the boot ROM exits.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ I'm not sure whether unmapped memory should be counted in assembly. Counting only payload data seems reasonable -- in which case you could have a 'hole' before the jump to the reset vector. You may want to ask about that on meta if it has not already been addressed. \$\endgroup\$
    – Arnauld
    Feb 17 at 20:35
  • 3
    \$\begingroup\$ (Actually, I think neither the input data nor the rst 0 should be taken into account for the byte count.) \$\endgroup\$
    – Arnauld
    Feb 18 at 9:15
9
\$\begingroup\$

J, 41 bytes

load'viewmat'
[:viewmat 2#._2|:@|.\#:@dfh

Try it online - fails because TIO can't run viewmat

Working link that outputs a matrix of integers 0 thru 3

Takes input as an array of hex strings.

  • Convert each hex string to a binary number
  • Take then in rows of two, reverse and transpose and convert back decimal
  • View the resulting matrix as an image

Image produced when running the function locally:

enter image description here

\$\endgroup\$
8
\$\begingroup\$

MATL, 13 bytes

8&B1L&Y)E+1YG

Inputs an array of bytes, and displays an image. The image consists of grey colours where 00 corresponds to black and 11 to white.

Try it at MATL Online!

How it works

8&B    % Implicit input: 1×16 array. Convert to binary with 8 bits.
       % Gives a 16×8 array
1L     % Push [1 2 j]. As an index, this is interpreted as 1:2:end 
&Y)    % Two-ouput row indexing. This gives an 8×8 subarray with the
       % indicated rows, and an 8×8 subarray with the remaining rows
E      % Multiply by 2, element-wise
+      % Add. This gives an 8×8 array with possible values 0, 1, 2, 3
1YG    % Display as an image with scaled colors, using the default
       % grey colormap
\$\endgroup\$
8
\$\begingroup\$

Shell + rgbds devkit, 16 bytes

rgbgfx -r1 -o- -

Takes raw bytes from stdin and outputs a png on stdout. rgbgfx is the graphics conversion program from the RGBDS Gameboy Development Kit, intended to convert PNGs to Gameboy tile data, and vice-versa.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Right tool for the job \$\endgroup\$
    – Jonah
    Feb 17 at 16:55
7
\$\begingroup\$

R, 65 bytes

\(a)image(t((b=outer(rev(a),2^(7:0),`%/%`)%%2)[!1:0,]+2*b[!0:1]))

Don't attempt This Online; instead, Try it at rdrr.io with graphical output (uses function instead of \ due to older version of R installed at rdrr.io).

Note that this could be 7 bytes shorter by omitting the image() call and outputting a matrix of bytes, but R has good graphical output so it seems more in the spirit of the challenge to actually draw the image.

\$\endgroup\$
7
\$\begingroup\$

QBasic, 100 96 bytes

SCREEN 7
FOR r=0TO 7
INPUT a
INPUT b
FOR c=0TO 7
PSET(7-c,r),a\2^c MOD 2+(1AND b\2^c)*3
NEXT c,r

You can try it on Archive.org. Input the bytes one at a time as decimal integers (0 to 255). The image is displayed in the upper left corner of the screen, overwriting the ? prompt from the first INPUT statement. Each "pixel" is 2x2. The colors are:

  • Black: rgb(0,0,0)
  • Blue: rgb(0,0,170)
  • Cyan: rgb(0,170,170)
  • Red: rgb(170,0,0)

Example run of the golfed code

Ungolfed

With a less golfy formula for computing the color code, and drawing each pixel as a filled box rather than a single point, it turns out we can reproduce the grayscale images in the OP exactly.

SCREEN 7
' Each point in screen mode 7 is 2x2 pixels, so we draw 15x15 boxes to match
' the 30x30 pixel boxes in OP's image
size = 15
FOR row = 0 TO 7
  INPUT lowbits
  INPUT highbits
  FOR col = 0 TO 7
    place = 2 ^ (7 - col)
    lowbit = (lowbits \ place) AND 1
    highbit = (highbits \ place) AND 1
    ' 00 = color 15, white
    ' 01 = color 7, light gray
    ' 10 = color 8, dark gray
    ' 00 = color 0, black
    colorcode = 15 - lowbit * 8 - highbit * 7
    ' Compute top and left coordinates of the box based on row and column
    ' (with the left shifted over to avoid drawing over the inputs)
    top = row * size
    left = 60 + col * size
    ' Draw the box using the LINE command with the BF (box filled) option
    LINE (left, top)-(left + size - 1, top + size - 1), colorcode, BF
  NEXT col
NEXT row

Example run of the ungolfed code

\$\endgroup\$
6
\$\begingroup\$

Japt -R, 17 25 23 bytes

m¤ùT8 ò ®y͸Ãi"P2 8 8 3

Try it

Saved two bytes thanks to Shaggy

Previously, this answer outputted a string using differently shaded unicode characters to represent the different colors, but as OP has clarified that this is not allowed, it now outputs a Plain PGM file.

Takes input as byte array and outputs a string representing a PGM file.

m¤ùT8 ò ®y͸Ãi"P2 8 8 3  : implicit input of byte array
m¤                       : convert each byte to binary
  ùT8                    : left-pad each binary string with 0s
      ò ®                : map each pair of lines to:
         yÍ              :   transpose the pair, map each two-char string to a number
           ¸             :   join by spaces
            Ã            : end map
             i"P2 8 8 3  : prepend the start of a PGM file
                         : -R join by newlines

enter image description here

Japt -R, 11 bytes

Outputs a matrix of bytes.

m¤ùT8 ò ®yÍ
\$\endgroup\$
9
  • \$\begingroup\$ The character set [9617,9618,9619,9608] works much better for me, but it may depend on the installed fonts. \$\endgroup\$
    – Arnauld
    Feb 17 at 11:42
  • \$\begingroup\$ @Arnauld Thanks, edited \$\endgroup\$
    – noodle man
    Feb 17 at 11:49
  • 1
    \$\begingroup\$ 25 bytes. Or, if we can take input in binary, you can remove the first 2 bytes from that. \$\endgroup\$
    – Shaggy
    Feb 17 at 12:56
  • 1
    \$\begingroup\$ @Shaggy No, I thought that was generally accepted for graphical-output challenges--but maybe I'm wrong. I just posted a comment on the challenge \$\endgroup\$
    – noodle man
    Feb 17 at 17:45
  • 1
    \$\begingroup\$ Great shout on using n2 instead of indexing into an array; kicking myself that I didn't catch that! \$\endgroup\$
    – Shaggy
    Feb 17 at 19:30
5
\$\begingroup\$

JavaScript (ES6), 79 bytes

Expects a list of bytes and outputs a matrix of RGB tuples, using the greenish palette 00C000, 008000, 004000, 000000.

a=>(v=[...'01234567']).map(y=>v.map(x=>[0,~a[y*2]<<x>>1&64|~a[y-~y]<<x&128,0]))

Try it online!

73 bytes

Outputs a matrix of bytes.

a=>(v=[...'01234567']).map(y=>v.map(x=>~a[y*2]<<x>>1&64|~a[y-~y]<<x&128))

Try it online!

Rendering

f=
a=>(v=[...'01234567']).map(y=>v.map(x=>[0,~a[y*2]<<x>>1&64|~a[y-~y]<<x&128,0]))

F = (id, a) => { m = f(a); m.forEach(r => r.forEach(rgb => { div = document.createElement('div'); div.style.backgroundColor = '#' + rgb.map(v => v.toString(16).padStart(2, '0')).join(''); document.getElementById(id).appendChild(div); })) }

F('b0', [0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF])
F('b1', [0xFF, 0x00, 0x7E, 0xFF, 0x85, 0x81, 0x89, 0x83, 0x93, 0x85, 0xA5, 0x8B, 0xC9, 0x97, 0x7E, 0xFF])
F('b2', [0x7C, 0x7C, 0x00, 0xC6, 0xC6, 0x00, 0x00, 0xFE, 0xC6, 0xC6, 0x00, 0xC6, 0xC6, 0x00, 0x00, 0x00])
F('b3', [0xFF, 0xFF, 0xFF, 0x81, 0xC3, 0x81, 0xDF, 0x85, 0xDF, 0x85, 0xFF, 0xBD, 0xFF, 0x81, 0xFF, 0xFF])
.box { float:left; margin-right:16px; width:128px; height:128px; } .box div { float:left; width:16px; height:16px; }
<div id="b0" class="box"></div><div id="b1" class="box"></div><div id="b2" class="box"></div><div id="b3" class="box"></div>

\$\endgroup\$
5
\$\begingroup\$

C, 108 105 95 93 89 85 bytes

b;main(j){for(puts("P5 8 8 3");j=j-1?:read(0,&b,2)*4;b+=b)putchar(3^b>>14&2^b>>7&1);}

Try it online!

Reads raw bytes from stdin and outputs an image in the PGM format to stdout.

-17! thanks to @c--

\$\endgroup\$
6
  • \$\begingroup\$ Suggest b+=b instead of b<<=1 and 3^b>>14&2^b>>7&1 instead of 3-(b>>14&2|b>>7&1) \$\endgroup\$
    – c--
    Feb 17 at 16:57
  • \$\begingroup\$ @c-- Thanks, I missed that. \$\endgroup\$
    – pan
    Feb 17 at 16:59
  • 1
    \$\begingroup\$ 2 more bytes can be saved using read(0,&b,2),i-- and for(puts("P2\n8 8 3");, actually you can get rid of i completely and rely on read(2) returning 0 in the end, which is 95 bytes \$\endgroup\$
    – c--
    Feb 17 at 17:23
  • 1
    \$\begingroup\$ you can merge the nested loops into 1 for -4 bytes. I'm not sure why, but I'm not getting the same output on every run \$\endgroup\$
    – c--
    Feb 18 at 20:05
  • 1
    \$\begingroup\$ @c-- Thanks! I had noticed too, but it's a TIO thing. It works as expected locally. \$\endgroup\$
    – pan
    Feb 18 at 20:38
3
\$\begingroup\$

K (ngn/k), 44 bytes

`0:"P2 8 8 3"
2/+,/+'0N 2#-8#'+(8#0),2\.0:`

Takes input as a space separated list of integers. Outputs a plain pgm to stdout.

Colors are inverted from the example.

\$\endgroup\$
2
\$\begingroup\$

Nibbles, 20.5 bytes (41 nibbles)

"P2 8 8 3 ".`/~.$``@hex:1$>>/$!@@~++

Input is an array of hex strings; output is a plain PGM image file (inspired by pan's answer).

"P2 8 8 3 "                 # prepend the string "P2 8 8 3 " onto:
    .                       # map over
     $                      # the input:
            :1$             #   prepend a '1'
         hex                #   then get the hex value
      ``@                   #   convert to bits
 `/                         # get chunks of 2
.                           # and map over these:
                 /$         #   fold across each chunk of 2
                   !@@      #   zipping-together the 2 elements
                      ~+    #   by adding together
                        +   #     the first elements added to themselves
                            #     and the second elements
               >>           #   and remove the first element     

enter image description here enter image description here


Note that if output as a matrix of bytes would be valid, we could drop the initial "P2 8 8 3 " header for the plain PGM format, for just 13 bytes.

\$\endgroup\$
0
2
\$\begingroup\$

JavaScript, 105 bytes

v=>{for(i=0;i<8;++i)for(j=0;j<8;++j)window['c'+i+(7-j)].className='wldb'[2*(b=n=>v[2*i+n]>>j&1)(0)+b(1)]}

Try it:

f=v=>{for(i=0;i<8;++i)for(j=0;j<8;++j)window['c'+i+(7-j)].className='wldb'[2*(b=n=>v[2*i+n]>>j&1)(0)+b(1)]}

button.addEventListener('click', _=>f(input.value.split` `.map(x=>+('0x'+x))));
.f {
  border: 1px solid black;
  width: fit-content;
}

.r {
  display: flex;
}

[id^="c"] {
  width: 40px;
  height: 40px;
  background-color: lightblue;
}

input {
  width: 320px;
}

.w {
  background-color: #fff;
}

.l {
  background-color: #555;
}

.d {
  background-color: #aaa;
}

.b {
  background-color: #000;
}
<input id="input" value="7C 7C 00 C6 C6 00 00 FE C6 C6 00 C6 C6 00 00 00">
<button id='button'>Draw</button>
<br><br>
<div class="f">
<div class="r">
  <div id="c00"></div><div id="c01"></div><div id="c02"></div><div id="c03"></div><div id="c04"></div><div id="c05"></div><div id="c06"></div><div id="c07"></div>
</div>
<div class="r">
  <div id="c10"></div><div id="c11"></div><div id="c12"></div><div id="c13"></div><div id="c14"></div><div id="c15"></div><div id="c16"></div><div id="c17"></div>
</div>
<div class="r">
  <div id="c20"></div><div id="c21"></div><div id="c22"></div><div id="c23"></div><div id="c24"></div><div id="c25"></div><div id="c26"></div><div id="c27"></div>
</div>
<div class="r">
  <div id="c30"></div><div id="c31"></div><div id="c32"></div><div id="c33"></div><div id="c34"></div><div id="c35"></div><div id="c36"></div><div id="c37"></div>
</div>
<div class="r">
  <div id="c40"></div><div id="c41"></div><div id="c42"></div><div id="c43"></div><div id="c44"></div><div id="c45"></div><div id="c46"></div><div id="c47"></div>
</div>
<div class="r">
  <div id="c50"></div><div id="c51"></div><div id="c52"></div><div id="c53"></div><div id="c54"></div><div id="c55"></div><div id="c56"></div><div id="c57"></div>
</div>
<div class="r">
  <div id="c60"></div><div id="c61"></div><div id="c62"></div><div id="c63"></div><div id="c64"></div><div id="c65"></div><div id="c66"></div><div id="c67"></div>
</div>
<div class="r">
  <div id="c70"></div><div id="c71"></div><div id="c72"></div><div id="c73"></div><div id="c74"></div><div id="c75"></div><div id="c76"></div><div id="c77"></div>
</div>
</div>

\$\endgroup\$
2
  • 5
    \$\begingroup\$ This is JavaScript+HTML+CSS, also I believe you have to include the CSS in the byte count since the JS wouldn't work without it \$\endgroup\$
    – noodle man
    Feb 17 at 17:47
  • \$\begingroup\$ @Jacob I think of it this way: JS needs to correctly set the class names for the elements in order to get the correct image, and HTML and CSS just help to see it in a pretty way :) \$\endgroup\$
    – EzioMercer
    Feb 17 at 19:33
2
\$\begingroup\$

Charcoal, 36 bytes

EE⁸⮌E²↨↧S¹⁶⮌⭆⁸§░▒▓█↨²﹪÷ιX²λ²

Try it online! Link is to verbose version of code. Now uses @Arnauld's output characters. Explanation:

  ⁸                             Literal integer `8`
 E                              Map over implicit range
     ²                          Literal integer `2`
    E                           Map over implicit range
        S                       Next input hex digit pair
       ↧                        Lowercased
      ↨  ¹⁶                     Converted from base `16`
   ⮌                            Reversed
E                               Map over each pair of hex values
             ⁸                  Literal integer `8`
            ⭆                   Map over implicit range and join
               ░▒▓█             Literal string
              §                 Indexed by
                       ι        Current pair
                      ÷         Vectorised divided by
                         ²      Literal integer `2`
                        X       Raised to power
                          λ     Current value
                     ﹪     ²    Vectorised modulo `2`
                   ↨²           Converted from base `2`
           ⮌                    Reversed
                                Implicitly print

Note that the deverbosifier on TIO erroneously uselessly quotes the special characters inside ”y and ; this is not actually necessary for correct operation of the program.

32 bytes by taking the input bytes with 0x prefix using the newer version of Charcoal on ATO:

EE⁸⮌E²N⮌⭆⁸§░▒▓█↨²﹪÷ιX²λ²

Attempt This Online! Link is to verbose version of code.

\$\endgroup\$
2
  • \$\begingroup\$ Could you save anything by taking input in decimal rather than hex? \$\endgroup\$
    – Shaggy
    Feb 17 at 13:28
  • \$\begingroup\$ @Shaggy I can save 4 bytes by using ATO. I could probably save another couple of bytes by making the input format even more convoluted. \$\endgroup\$
    – Neil
    Feb 17 at 13:55
2
\$\begingroup\$

C (GCC), 110 109 93 bytes

-16 bytes thanks to @c--

m;main(h){for(;m=m/2?:read(!puts(""),&h,2)*64;)printf("[%dm ",(h&m?39:99)+(h>>8&m?1:8));}

Attempt This Online!

Takes the input as a binary array from stdin, outputs spaces with different ANSI-colored background.

If the input is longer than 16 bytes, it continues displaying 8-pixel rows (which is incidentally useful for testing).

\$\endgroup\$
0
2
\$\begingroup\$

Befunge-with-graphics, 149 bytes

Takes raw bytes from stdin and creates a window which displays the result.

99*:sfcu88803v
v_v#!:-1\~<+p<
$ >       ^
>802p>:2%\2/\01p\:2%\2/\01g2*+3\-89+5v
$    ^$p20_v#:-1g20-1g30\xg30g20f::**<
^$p30_v#:$ <
v-1z<u<
>  #^_@

Test case:
Output image

\$\endgroup\$
1
  • \$\begingroup\$ Very cool extension to befunge! \$\endgroup\$
    – noodle man
    May 21 at 11:48
2
\$\begingroup\$

SAS

136 - ascii art in Log

This solution uses the idea from Charcoal language with help of special characters: " ░▒▓" of "gradient dots", it is 11 bytes, the firs symbol is a space.

The Code:

data;input(a b)(:hex2.)@@;do x=1to 8;c=ksubstr(" ░▒▓",1+2*char(put(a,binary8.),x)+char(put(b,binary8.),x),1);put c+(-1)@;end;put;cards;;

Input goes between last two semicolons as a string e.g., FF 00 7E FF 85 81 89 83 93 85 A5 8B C9 97 7E FF

It allows to use multiple strings at once, so if there are any other test cases I gladly add then to display.

In human readable form it looks like:

data;
  input (a b) (:hex2.) @@;
  do x = 1 to 8;
    c = ksubstr(" ░▒▓"
               ,1 + 2*char(put(a, binary8.), x) + char(put(b, binary8.), x)
               ,1);
    put c +(-1) @;
  end;
  put;
cards;
FF 00 7E FF 85 81 89 83 93 85 A5 8B C9 97 7E FF
7C 7C 00 C6 C6 00 00 FE C6 C6 00 C6 C6 00 00 00
;

In the log you will see:

▒▒▒▒▒▒▒▒
░▓▓▓▓▓▓░
▓    ▒ ▓
▓   ▒ ░▓
▓  ▒ ░▒▓
▓ ▒ ░▒░▓
▓▒ ░▒░░▓
░▓▓▓▓▓▓░
 ▓▓▓▓▓  
░░   ░░ 
▒▒   ▒▒ 
░░░░░░░ 
▓▓   ▓▓ 
░░   ░░ 
▒▒   ▒▒ 
        

180 = 113+67 - heatmap

Since SAS at its origin stand for Statistical Analysis System let's use "three dimensional" histogram i.e. HeatMap

The code

  • part 1)
data;input(a b)(:hex2.)@@;y+(-1);do x=1to 8;c=2*char(put(a,binary8.),x)+char(put(b,binary8.),x);output;end;cards;
  • part 2)
;proc sgplot;heatmap x=x y=y/colorresponse=c nxbins=8 nybins=8;run;

Input goes between past 1) and 2) as a string e.g., FF 00 7E FF 85 81 89 83 93 85 A5 8B C9 97 7E FF

Human readable form looks like:

data;
  input (a b) (:hex2.) @@;
  y + (-1);
  do x = 1 to 8;
    c = 2*char(put(a, binary8.), x) + char(put(b, binary8.), x);
    output;
  end;
cards;
FF 00 7E FF 85 81 89 83 93 85 A5 8B C9 97 7E FF
;
proc sgplot;
  heatmap x=x y=y / 
    colorresponse=c nxbins=8 nybins=8
  ;
run;

The sgplot procedure uses its default setup and the output looks like: enter image description here

Wit some "polishing"

proc sgplot noautolegend;
  heatmap x=x y=y / 
    colorresponse=c nxbins=8 nybins=8 
    colormodel=(lightgrey black);
  xaxis display=none;
  yaxis display=none;
run;

the output looks like: enter image description here

and

enter image description here

\$\endgroup\$
2
\$\begingroup\$

Vyxal, 26 21 bytes

bṅvṅ2ẇv∩vvB883f‛P2pp⁋

Try it Online!

Uses roughly the same method as my Japt answer. I have tried a lot of different things but I can't find a 20-byter. With Vyncode this is 17 bytes but I'm not the biggest fan of that.

Takes input as a list of bytes, outputs a Plain PGM file.

Explanation:

bṅvṅ2ẇv∩vvB883f‛P2pp⁋­⁡​‎‎⁡⁠⁡‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁢‏⁠⁠‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁣‏⁠‎⁡⁠⁤‏‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏‏​⁡⁠⁡‌⁢⁢​‎‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏‏​⁡⁠⁡‌⁢⁣​‎‎⁡⁠⁣⁤‏⁠‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏‏​⁡⁠⁡‌⁢⁤​‎‎⁡⁠⁤⁤‏⁠‎⁡⁠⁢⁡⁡‏⁠‎⁡⁠⁢⁡⁢‏‏​⁡⁠⁡‌⁣⁡​‎‎⁡⁠⁢⁡⁣‏‏​⁡⁠⁡‌⁣⁢​‎‎⁡⁠⁢⁡⁤‏⁠‏​⁡⁠⁡‌⁣⁣​‎‎⁡⁠⁢⁢⁡‏‏​⁡⁠⁡‌­
b                      # ‎⁡Convert to list of binary digits
 ṅ                     # ‎⁢join each to a string
  vṅ                   # ‎⁣pad each binary string to length 8 with zeroes
    2ẇ                 # ‎⁤chunks of length 2
      v∩               # ‎⁢⁡transpose each pair to a list of two-bit binary strings
        vvB            # ‎⁢⁢convert each from binary
           883f        # ‎⁢⁣digits of 883
               ‛P2     # ‎⁢⁤string literal “P2”
                  p    # ‎⁣⁡prepend this to digit list
                   p   # ‎⁣⁢prepend this header to the matrix
                    ⁋  # ‎⁣⁣join each item on spaces and then join the items on newlines
💎

Created with the help of Luminespire.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ I think you'll have to update this, like your Japt answer and my Thunno answer. \$\endgroup\$
    – The Thonnu
    Feb 20 at 16:57
  • 1
    \$\begingroup\$ @TheThonnu Yeah I'm just really lazy lol. I'll do it later today, shouldn't take more than a few minutes \$\endgroup\$
    – noodle man
    Feb 20 at 20:25
  • \$\begingroup\$ @UndoneStudios done \$\endgroup\$
    – noodle man
    Feb 23 at 14:58
  • \$\begingroup\$ Vyxal?! You traitor! :p Just as I was beginning to think you were my new Japt protégé! \$\endgroup\$
    – Shaggy
    Feb 23 at 23:37
  • 1
    \$\begingroup\$ Been toying with the idea myself for years but, not having the first clue about language design, I never got around to it. But I'd absolutely pitch in with you to help out. ETH had a lot of ideas for v2 of Japt before he disappeared that would have made it so much better; things like a custom codepage and being able to omit closing spaces for methods with a fixed arity. \$\endgroup\$
    – Shaggy
    Feb 24 at 14:35
2
\$\begingroup\$

Go, 304 302 bytes

import(."image";c"image/color";d"image/draw";p"image/png";."os")
func f(s[]byte){N,S,R,D,P:=NewUniform,d.Src,Rect,d.Draw,Point{0,0}
T:=NewGray(R(0,0,8,8))
for i:=0;i<8;i++{for l,h,j:=s[2*i],s[2*i+1],0;j<8;j++{D(T,R(j,i,j+1,i+1),N(c.Gray{255-85*(h&128>>6+l&128>>7)}),P,S)
h*=2
l*=2}}
p.Encode(Stdout,T)}

Attempt This Online!

Prints the bytes of a PNG to STDOUT.

  • -2 bytes by @The Thonnu

Slightly-Ungolfed Explanation

import(."image";c"image/color";d"image/draw";p"image/png";."os")
func f(s[]byte){
N,S,R,D,P:=NewUniform,d.Src,Rect,d.Draw,Point{0,0}
                                      // various variables
T:=NewGray(R(0,0,8,8))                // new 8-bit grayscale, 8x8px
for i:=0;i<8;i++{                     // for each row...
l,h:=s[2*i],s[2*i+1]                  // get the bytes for that row
for j:=0;j<8;j++{                     // for each pixel...
C:=c.Gray{255-85*(h&128>>6+l&128>>7)} // calculate the color.
                                      // yields an inverted color, so `255-x` is needed.
D(T,R(j,i,j+1,i+1),N(C),P,S)          // draw the pixel to the tile
h<<=1;l<<=1}}                         // shift the bytes left to get the next pixel.
p.Encode(Stdout,T)}                   // print the PNG to STDOUT
\$\endgroup\$
1
2
\$\begingroup\$

Thunno N, \$ 30 \log_{256}(96) \approx \$ 24.69 bytes

b8zK2ApeZt.JBA_sjE"P2 8 8 3"ZN

Attempt This Online!

Port of Jacob noodle man's Japt answer.

-3 characters thanks to noodle man

Explanation

b           # Convert each number in the (implicit) input list to binary
 8zK        # Fill each with zeros to length 8
    2Ap     # Split the list into pairs
       e    # Map over this list of pairs:
        Zt  #  Transpose the pair
.J          #  And join each inner list into a string
  B         #  Convert each string from binary
   A_sj     #  And join this list of strings by spaces
       E    # (End map)
"..."ZN     # Prepend the string "P2 8 8 3"
            # Implicit output, joined on newlines
\$\endgroup\$
4
  • \$\begingroup\$ Unfortunately the OP wrote after I posted my answer that it isn't allowed to output using a string like that, so this is invalid. See my updated Japt answer \$\endgroup\$
    – noodle man
    Feb 18 at 18:38
  • \$\begingroup\$ @Jacob thanks for the heads-up. I've updated my answer. \$\endgroup\$
    – The Thonnu
    Feb 18 at 20:17
  • \$\begingroup\$ Thunno N, 30 characters: b8zK2ApeZt.JBA_sjE"P2 8 8 3"ZN \$\endgroup\$
    – noodle man
    Aug 5 at 18:48
  • \$\begingroup\$ @noodleman nice! It's been so long since I last wrote a program in Thunno so it felt kinda cool writing the explanation. \$\endgroup\$
    – The Thonnu
    Aug 5 at 19:47
2
\$\begingroup\$

Python, 526 524 483 471 470 454 447 bytes

I'm a turtle veteran. For fast results, use speed(0). Uses the given pallet.

def g(l,x=range(8)):
 r=[int(i,16)for i in l];j=[[r[i+1],r[i]]for i in range(0,16,2)]
 for i in x:j[i]=["".join([bin(j[i][0])[2:].zfill(8).replace(" ","0")[k],bin(j[i][1])[2:].zfill(8).replace(" ","0")[k]])for k in x]
 for i in x:
  for l in x:j[i][l]=int(j[i][l],2);j[i][l]="#"+"FA50"[j[i][l]]*6
 for i in j:
  for k in i:seth(0);color(k);begin_fill();exec('fd(9);rt(90);'*4);end_fill();fd(9)
  pu();goto(0,ycor()-9);pd();ht()
from turtle import*

Test case 1:
enter image description here

Test case 2:
enter image description here

\$\endgroup\$
1
1
\$\begingroup\$

Clojure, 316 bytes

(fn[i](doto(Frame.)(.add(proxy[Panel][](paint[g](dotimes[r 8](dotimes[c 8](.setColor g(((mapv(fn[[l h]](mapv #(Color.({0 0xFFFFFF 1 0xAAAAAA 2 5592405 3 0}(Long/parseLong(str % %2)2)))h l))(partition 2(map #(clojure.pprint/cl-format nil "~8,'0b"%)i)))r)c))(.fillRect g(* c 9)(* r 9)9 9)))))).pack(.setVisible true)))

Inputs:

  1. [0xFF, 0x00, 0x7E, 0xFF, 0x85, 0x81, 0x89, 0x83, 0x93, 0x85, 0xA5, 0x8B, 0xC9, 0x97, 0x7E, 0xFF]

  2. [0x7C, 0x7C, 0x00, 0xC6, 0xC6, 0x00, 0x00, 0xFE, 0xC6, 0xC6, 0x00, 0xC6, 0xC6, 0x00, 0x00, 0x00]

Outputs:

enter image description here

\$\endgroup\$
1
\$\begingroup\$

Postscript 245 168

Images in Postscript can have 2 bits per pixel, or, more specifically, 2 bits per color component and 1 component (e.g. Grayscale) per sample. However, interpreter expects bytes in data source to constitute a continuous bit stream, not low bits here, high bits there. We'll have to zip bits from each two bytes:

currentpagedevice /PageSize get aload pop scale

<FF 00 7E FF 85 81 89 83 93 85 A5 8B C9 97 7E FF>

% 16 byte string on stack here
{not} forall                % 16 bytes
8 8 2 [8 0 0 8 0 0]
{                           % consume by pairs (a, b)
    0                       % accumulator (x)
    0 1 7 {                 % a b x s
        dup dup             % a b x s s s
        1 exch bitshift     % a b x s s m
        dup 5 index and     % a b x s s m bm
        4 1 roll            % a b x bm s s m
            6 index and     % a b x bm s s am
        exch bitshift       % a b x bm s ax
        3 1 roll            % a b x ax bm s
        1 add bitshift      % a b x ax bx
        or or               % a b x
    } for
    3 1 roll pop pop        % x

    dup  256 idiv
    exch 255 and            % 2 bytes

    2 string dup 0 5 -1 roll put
             dup 1 4 -1 roll put
}
image

I'll use "16 byte string already on stack" for simplicity as allowed in the OP, though it could have been read from file (e.g. STDIN) or command line. I think it's OK for program size byte count to begin after 1st comment, and then stripped of insignificant whitespace it would be:

{not}forall 8 8 2[8 0 0 8 0 0]{0 0 1 7{dup dup 1 exch bitshift dup 5 index and 4 1 roll 6 index and exch bitshift 3 1 roll 1 add bitshift or or}for 3 1 roll pop pop dup 256 idiv exch 255 and 2 string dup 0 5 -1 roll put dup 1 4 -1 roll put}image

The very 1st line was not included in count as it just serves to produce more "pleasing" output. Command (gs in Linux)

gswin64c -q -g200x200 source.ps

(again, image size on command line is only for better looking square tile, instead of scaled non-proportionately to A4 (or Letter) default page).

enter image description here

For golfing purposes, long repeatedly used operators could be re-defined as short-named procedures, or entire source compactly written as binary tokens, though result would be not very readable. I have better idea: to produce the same output, let's draw not one 2 ppx image, but three 1 ppx image masks to paint with 0.33, 0.66 and 1.0 ink, respectively. Then, e.g., if a bit is set in low-bits byte and I paint this pixel with light-gray, it won't interfere with painting later in dark-gray and/or black (if bit is set in high-bits byte).

currentpagedevice /PageSize get aload pop scale

<FF 00 7E FF 85 81 89 83 93 85 A5 8B C9 97 7E FF>

% 16 byte string on stack here
{} forall
16 -1 9 { 
    dup index
    exch 1 sub index 
    and
} for
23 -1 16 { 
    -1 roll 
} for
24 -8 roll 

2 -1 0 {
    3 div setgray
    8 8 true [8 0 0 8 0 0]
    { 1 string dup 0 4 -1 roll put }
    imagemask
} for

I.e.:

{}forall 16 -1 9{dup index exch 1 sub index and}for 23 -1 16{-1 roll}for 24 -8 roll 2 -1 0{3 div setgray 8 8 true[8 0 0 8 0 0]{1 string dup 0 4 3 roll put}imagemask}for

There is bytes moving around stack instead of bits juggling, which is significantly less cumbersome, and shorter.

\$\endgroup\$
1
\$\begingroup\$

Ruby, 101 bytes

Returns the lines of a PGM file. I'm sure that each_slice could be golfed out but I don't feel like it. Could be a byte shorter by removing the splat before a.map but I don't like returning a ragged array.

->a{["P2 8 8 3",*a.map{("%08b"%_1).chars}.each_slice(2).map{_1.transpose.map{|x|x.join.to_i 2}*" "}]}

Attempt This Online!

\$\endgroup\$
0
\$\begingroup\$

Thunno 2 N, 22 bytes

ḃ8ṛ2ẇıṬ€JḂ;883`P2ƤƤðȷj

Try it online!

Port of noodle man's Japt answer.

Explanation

ḃ8ṛ2ẇıṬ€JḂ;883`P2ƤƤðȷj  # Implicit input
ḃ8ṛ                     # Convert each to binary and pad with zeros to length 8
   2ẇı    ;             # To each chunk of 2:
      Ṭ€J               #  Transpose and join each
         Ḃ              #  Then convert back from binary
           883          # Push 883
              `P2Ƥ      # Prepend "P2"
                  Ƥ     # And prepend this header
                   ðȷj  # Join each inner list on spaces
                        # Implicit output, joined on newlines 
\$\endgroup\$
5
  • \$\begingroup\$ I think it has to start with “P2 8 8 3” not “P2883” but I can’t check on my phone. (My vyxal answer pushes ["P2",8,8,3] and joins on spaces.) \$\endgroup\$
    – noodle man
    Jul 31 at 17:04
  • \$\begingroup\$ @noodleman oh yeah, fixed. \$\endgroup\$
    – The Thonnu
    Jul 31 at 17:27
  • \$\begingroup\$ Who’s Jacob, anyway? \$\endgroup\$
    – noodle man
    Jul 31 at 17:31
  • \$\begingroup\$ For some reason, whenever I see your display name, I always think "Jacob" :p \$\endgroup\$
    – The Thonnu
    Jul 31 at 17:34
  • \$\begingroup\$ Makes sense. I was thinking about changing it back (it gets confusing when my GitHub account has my real name) but I think I’ll keep it for a bit longer \$\endgroup\$
    – noodle man
    Jul 31 at 17:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.