Skip to main content
Skip table of contents

C48: Wide Range, High Bit Depth Colour

In C24, which is our standard operating mode, the red, green and blue components of a pixel are assigned 8 bits each. This means that each colour has 28 or 256 levels of intensity (0-255). Most commercial displays are 8 bits per colour (bpc).

In C48, data from horizontally adjacent pixels are combined so that each colour channel receives 16 bits instead. The leftmost pixel of the pair contributes the 8 most significant bits, and the rightmost pixel contributes the 8 least significant bits. 

Bit assignment in C24 (standard mode) and C48 (combined horizontal pixels)

At 16 bpc, your red, green and blue colour channels now have 216 or 65,536 levels of intensity (0-65535). However, as a consequence of this pixel combination your image’s horizontal resolution is halved. So if your 8 bpc stimuli were drawn in full HD (1920 x 1080 pixels), your 16 bpc image will be reduced to 960 x 1080 pixels.

Our hardware will present this 16 bpc image data full screen, full resolution on the display. The image data is scaled to double-width to get the final display output.

This means that if you do nothing to adjust your stimuli in C48, they will appear horizontally stretched on your display.

Half-resolution image data is shown full screen on a VIEWPixx display

If you are working in MATLAB or Octave, there are several PsychImaging functions to simplify working in C48 mode. These functions allow you to draw your images directly in 16 bpc, so you don’t need to worry about combining 8 bpc pixels yourself. The backend graphics management will then handle formatting the image data for our hardware. These PsychImaging functions also offer some different options for rescaling your stimuli automatically so they don’t appear stretched. For more on scaling options, see our C48 examples below.

The major advantage of C48 is having access to the full spectrum of colours on each video frame. This is a great mode for rich multicoloured images, complex gradients or colour wheels, and colour- or luminance-based visual search tasks where a fine degree of control is needed. 

Examples

Drawing a simple luminance patch in C48

Note this example requires the use of Psychtoolbox for MATLAB. In this example, we enable drawing in 16 bpc and C48 mode. Our stimulus is a luminance patch with a randomly selected 16 bpc gray level on a black background. We would like our output to be square, so we manually scaled our patch width to half the height. This ensures the stimulus will be stretched to the desired dimensions on our display.

MATLAB
%Establish the correct colour and video settings
AssertOpenGL;
PsychImaging('PrepareConfiguration');

%Draw stimuli in 16 bits per colour
PsychImaging('AddTask', 'General', 'FloatingPoint32Bit');

%Enable C48 and set the framebuffer to half the display resolution (no automatic scaling)
PsychImaging('AddTask', 'General', 'EnableDataPixxC48Output', 0);

%If using the PROPixx - uncomment the following to change the sequencer 
%for linearized output (trades off luminance). Make sure to reset to 0 at
%the end of your script.
%Datapixx('SetPropixxDlpSequenceProgram', 6);
%Datapixx('RegWr');

%Open a full-screen window with a black background
screenNumber = max(Screen('Screens'));
[win, rect] = PsychImaging('OpenWindow', screenNumber, [0,0,0]);

%Set up some position parameters
center = [rect(3)/2, rect(4)/2];
patchHeight = 200;
patchWidth = patchHeight/2; %rescale to half-width, so our final stretched image appears square
patchRect = [center(1) - patchWidth/2,  center(2) - patchHeight/2, ...
            center(1) + patchWidth/2,  center(2) + patchHeight/2];

%Let's pick a random gray level between 0-65535, which is our maximum value at 16 bpc. This must
%be expressed as a value from 0-1.
maxIntensity = (2^16)-1;
colourValue = randi(maxIntensity)/maxIntensity;
        
%Draw our patch and flip
Screen('FillRect', win, colourValue, patchRect);
Screen('Flip', win);

WaitSecs(4);

%Close window
Screen('Closeall');

Drawing a gradient of dots in C48 mode, using automatic scaling

In this example, we are using the Screen(‘DrawDots’) command to draw a gradient of dots in a ring around the middle of the display.  Like the previous example, we will draw our stimuli directly in 16 bpc.

DrawDots does not allow you to manually scale dots to half-width. Instead, we will use one of the automatic scaling modes available with PsychImaging(‘AddTask’, ‘General’, ‘EnableDataPixxC48Output’, modes). This will ensure our dots appear as circles on the display, rather than stretched ovals.

As of April 2022, there are three scaling modes to choose from:

Mode

Description

0

Image framebuffer is set to half the display resolution. This preserves 100% of whatever you draw, but if you do not manually scale your stimuli (as in example 1) they will appear stretched on the final full resolution display.

1

Image framebuffer is set to full display resolution. When the half-resolution image data is generated to send to the display, odd pixels in the framebuffer are ignored. Stimuli appear with the correct dimensions on the final full-resolution display. 

2

Image framebuffer is set to full display resolution. When the half-resolution image data is generated to send to the display, the 16 bpc values of adjacent pixels are averaged to a single value. Stimuli appear with the correct dimensions on the final full-resolution display.

Which of these modes you use depends on your stimuli and what you are trying to achieve. Here we will use mode 2, as the pixel averaging creates some nice antialiasing around the edges of our dots and makes them look smoother.  

MATLAB
%Establish the correct cocolournd video settings
AssertOpenGL;

Screen('Preference', 'SkipSyncTests', 1);
PsychImaging('PrepareConfiguration');
PsychImaging('AddTask', 'General', 'FloatingPoint32Bit');

%Our stimuli are circular and hard to scale manually, so we will use mode 2, which averages adjacent
%pixels to scale the image for us. You can also use mode 1, but mode 2 gives some nice antialiasing around
%the edges of circular stimuli.
PsychImaging('AddTask', 'General', 'EnableDataPixxC48Output', 2);

%If using the PROPixx - uncomment the following to change the sequencer 
%for linearized output (trades off luminance). Make sure to reset to 0 at
%the end of your script.
%Datapixx('SetPropixxDlpSequenceProgram', 6);
%Datapixx('RegWr');

%Open a full-screen window with a black background
screenNumber = max(Screen('Screens'));
[win, rect] = PsychImaging('OpenWindow', screenNumber, [0,0,0]);

%Set up some parameters
bitDepth = 16;
maxIntensity = 2^bitDepth;
numDots = 400;
radius = 400; %pixels
radiusCenter = [rect(3)/2, rect(4)/2];
dotSize = 10;
theta = (pi*2)/numDots;

%Initialize dot parameters
positions = nan(2,numDots);
colours = nan(3,numDots);

%A simple loop to assign dot positions and create an evenly-spaced red gradient
for k=1:numDots
    positions(1, k) = radius * cos(theta*k);
    positions(2, k) = radius * sin(theta*k);
    colours(:, k) = [((maxIntensity/numDots)*k)/maxIntensity,0,0];
end

%Draw all of our dots and flip
Screen('DrawDots', win, positions, dotSize, colours, radiusCenter, 1);
Screen('Flip', win);

WaitSecs(4);

%Close window
Screen('Closeall');

Not sure if you are actually in C48 mode? Psychtoolbox prints detailed information to the command line whenever a new window is opened; under “PTB – Info” you should see C48 and your scaling mode identified. You can also check in PyPixx under “Video Mode Configuration” (while the window is open), or by disabling any scaling and confirming your stimuli are horizontally stretched.

These methods will confirm what video mode the hardware is in. Only an external measurement tool like a colorimeter or spectrophotometer can externally verify your display colour/luminance output. 

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.