1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
/*
* PsychToolbox3/Source/Common/Screen/SCREENPutImage.c
*
* AUTHORS:
*
* Allen.Ingling@nyu.edu awi
* mario.kleiner.de@gmail.com mk
*
* PLATFORMS:
*
* All.
*
* HISTORY:
*
* 02/06/03 awi Created.
* 10/12/04 awi In useString: moved commas to inside []. Filled in synopsis
* 1/14/05 awi Commented out calls to enable alpha blending.
* 2/25/05 awi Added call to PsychUpdateAlphaBlendingFactorLazily(). Drawing now obeys settings by Screen('BlendFunction').
* 2/25/07 cgb Now lives in the GLfloat world.
* 2/01/08 mk Add missing glPixelZoom(1,1); to avoid side effects.
* 5/01/08 mk Remove check for target windows number of layers - This works for all layer values as the GL handles this.
* 3/20/11 mk Make 64-bit clean.
*/
#include "Screen.h"
// If you change the useString then also change the corresponding synopsis string in ScreenSynopsis.c
static char useString[] = "Screen('PutImage', windowPtr, imageArray [,rect])";
// 1 2 3
static char synopsisString[] =
"Copy the matrix \"imageArray\" to a window, very slowly.\n"
"This function is not (and will not ever get) supported on OpenGL-ES embedded graphics hardware.\n"
"\"rect\" is in window coordinates. The whole image is copied to \"rect\", scaling if "
"necessary. The rect default is the imageArray's rect, centered in the window.\n"
"The orientation of the array in the window is identical to that of Matlab's "
"numerical array displays in the Command Window. The first pixel is in the upper "
"left, and the rows are horizontal. imageArray can be a pure luminance matrix, "
"a RGB color matrix, or a RGBA color matrix with alpha channel. Its data type "
"can be uint8 (faster) or double. Please note that this function is relatively "
"slow and inflexible. Have a look at the 'MakeTexture' and 'DrawTexture' "
"functions for a faster and more flexible way of drawing image matrices. ";
static char seeAlsoString[] = "GetImage OpenOffscreenWindow MakeTexture DrawTexture DrawTextures";
// Macro version of a function found in MiniBox.c. Eliminates the unneeded overhead required by a function call.
// This improves speed by several milliseconds for medium to large images.
#define PSYCHINDEXELEMENTFROM3DARRAY(mDim, nDim, pDim, m, n, p) (p*mDim*nDim + n*mDim + m)
PsychError SCREENPutImage(void)
{
PsychRectType windowRect, positionRect;
int ix, iy;
size_t matrixRedIndex, matrixGreenIndex, matrixBlueIndex, matrixAlphaIndex, matrixGrayIndex;
int inputM, inputN, inputP, positionRectWidth, positionRectHeight;
size_t pixelIndex = 0;
PsychWindowRecordType *windowRecord;
unsigned char *inputMatrixByte;
double *inputMatrixDouble;
GLfloat *pixelData;
GLfloat matrixGrayValue, matrixRedValue, matrixGreenValue, matrixBlueValue, matrixAlphaValue;
PsychArgFormatType inputMatrixType;
GLfloat xZoom = 1, yZoom = -1;
// All sub functions should have these two lines.
PsychPushHelp(useString, synopsisString, seeAlsoString);
if (PsychIsGiveHelp()) {
PsychGiveHelp();
return PsychError_none;
};
// Cap the number of inputs.
PsychErrorExit(PsychCapNumInputArgs(4)); //The maximum number of inputs
PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs
// Get the image matrix.
inputMatrixType = PsychGetArgType(2);
switch (inputMatrixType) {
case PsychArgType_none:
case PsychArgType_default:
PsychErrorExitMsg(PsychError_user, "imageArray argument required");
break;
case PsychArgType_uint8:
PsychAllocInUnsignedByteMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixByte);
break;
case PsychArgType_double:
PsychAllocInDoubleMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixDouble);
break;
default:
PsychErrorExitMsg(PsychError_user, "imageArray must be uint8 or double type");
break;
}
if (inputP != 1 && inputP != 3 && inputP != 4) {
PsychErrorExitMsg(PsychError_user, "Third dimension of image matrix must be 1, 3, or 4");
}
// Get the window and get the rect and stuff.
PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
// A no-go on OES:
if (PsychIsGLES(windowRecord)) {
PsychErrorExitMsg(PsychError_unimplemented, "Sorry, Screen('PutImage') is not supported on OpenGL-ES embedded graphics hardware. Use 'MakeTexture' and 'DrawTexture' instead.");
}
PsychGetRectFromWindowRecord(windowRect, windowRecord);
if (PsychCopyInRectArg(3, FALSE, positionRect)) {
if (IsPsychRectEmpty(positionRect)) {
return PsychError_none;
}
positionRectWidth = (int) PsychGetWidthFromRect(positionRect);
positionRectHeight = (int) PsychGetHeightFromRect(positionRect);
if (positionRectWidth != inputN || positionRectHeight != inputM) {
// Calculate the zoom factor.
xZoom = (GLfloat) positionRectWidth / (GLfloat) inputN;
yZoom = -((GLfloat) positionRectHeight / (GLfloat) inputM);
}
}
else {
positionRect[kPsychLeft] = 0;
positionRect[kPsychTop] = 0;
positionRect[kPsychRight] = inputN;
positionRect[kPsychBottom] = inputM;
PsychCenterRect(positionRect, windowRect, positionRect);
}
// Allocate memory to hold the pixel data that we'll later pass to OpenGL.
pixelData = (GLfloat*) PsychMallocTemp(sizeof(GLfloat) * (size_t) inputN * (size_t) inputM * 4);
// Loop through all rows and columns of the pixel data passed from Matlab, extract it,
// and stick it into 'pixelData'.
for (iy = 0; iy < inputM; iy++) {
for (ix = 0; ix < inputN; ix++) {
if (inputP == 1) { // Grayscale
// Extract the grayscale value.
matrixGrayIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 1, (size_t) iy, (size_t) ix, 0);
if (inputMatrixType == PsychArgType_uint8) {
// If the color range is > 255, then force it to 255 for 8-bit values.
matrixGrayValue = (GLfloat)inputMatrixByte[matrixGrayIndex];
matrixGrayValue /= (GLfloat)255;
}
else {
matrixGrayValue = (GLfloat)(inputMatrixDouble[matrixGrayIndex] / fabs(windowRecord->colorRange));
}
// RGB will all be the same for grayscale. We'll go ahead and fix alpha to the max value.
pixelData[pixelIndex++] = matrixGrayValue; // R
pixelData[pixelIndex++] = matrixGrayValue; // G
pixelData[pixelIndex++] = matrixGrayValue; // B
pixelData[pixelIndex++] = (GLfloat) 1.0; // A
}
else if (inputP == 3) { // RGB
matrixRedIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 0);
matrixGreenIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 1);
matrixBlueIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 2);
if (inputMatrixType == PsychArgType_uint8) {
// If the color range is > 255, then force it to 255 for 8-bit values.
matrixRedValue = (GLfloat)inputMatrixByte[matrixRedIndex];
matrixGreenValue = (GLfloat)inputMatrixByte[matrixGreenIndex];
matrixBlueValue = (GLfloat)inputMatrixByte[matrixBlueIndex];
matrixRedValue /= (GLfloat)255;
matrixGreenValue /= (GLfloat)255;
matrixBlueValue /= (GLfloat)255;
}
else {
matrixRedValue = (GLfloat)(inputMatrixDouble[matrixRedIndex] / fabs(windowRecord->colorRange));
matrixGreenValue = (GLfloat)(inputMatrixDouble[matrixGreenIndex] / fabs(windowRecord->colorRange));
matrixBlueValue = (GLfloat)(inputMatrixDouble[matrixBlueIndex] / fabs(windowRecord->colorRange));
}
pixelData[pixelIndex++] = matrixRedValue;
pixelData[pixelIndex++] = matrixGreenValue;
pixelData[pixelIndex++] = matrixBlueValue;
pixelData[pixelIndex++] = (GLfloat)1.0;
}
else if (inputP == 4) { // RGBA
matrixRedIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 0);
matrixGreenIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 1);
matrixBlueIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 2);
matrixAlphaIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 3);
if (inputMatrixType == PsychArgType_uint8) {
// If the color range is > 255, then force it to 255 for 8-bit values.
matrixRedValue = (GLfloat)inputMatrixByte[matrixRedIndex];
matrixGreenValue = (GLfloat)inputMatrixByte[matrixGreenIndex];
matrixBlueValue = (GLfloat)inputMatrixByte[matrixBlueIndex];
matrixAlphaValue = (GLfloat)inputMatrixByte[matrixAlphaIndex];
matrixRedValue /= (GLfloat)255;
matrixGreenValue /= (GLfloat)255;
matrixBlueValue /= (GLfloat)255;
matrixAlphaValue /= (GLfloat)255;
}
else {
matrixRedValue = (GLfloat)(inputMatrixDouble[matrixRedIndex] / fabs(windowRecord->colorRange));
matrixGreenValue = (GLfloat)(inputMatrixDouble[matrixGreenIndex] / (GLfloat)fabs(windowRecord->colorRange));
matrixBlueValue = (GLfloat)(inputMatrixDouble[matrixBlueIndex] / (GLfloat)fabs(windowRecord->colorRange));
matrixAlphaValue = (GLfloat)(inputMatrixDouble[matrixAlphaIndex] / (GLfloat)fabs(windowRecord->colorRange));
}
pixelData[pixelIndex++] = matrixRedValue;
pixelData[pixelIndex++] = matrixGreenValue;
pixelData[pixelIndex++] = matrixBlueValue;
pixelData[pixelIndex++] = matrixAlphaValue;
}
} // for (iy = 0; iy < inputM; iy++)
} // for (ix = 0; ix < inputN; ix++)
// Enable this windowRecords framebuffer as current drawingtarget:
PsychSetDrawingTarget(windowRecord);
// Disable draw shader:
PsychSetShader(windowRecord, 0);
PsychUpdateAlphaBlendingFactorLazily(windowRecord);
// Set the raster position so that we can draw starting at this location.
glRasterPos2f((GLfloat)(positionRect[kPsychLeft]), (GLfloat)(positionRect[kPsychTop]));
// Tell glDrawPixels to unpack the pixel array along GLfloat boundaries.
glPixelStorei(GL_UNPACK_ALIGNMENT, (GLint) sizeof(GLfloat));
// Dump the pixels onto the screen.
glPixelZoom(xZoom, yZoom);
glDrawPixels(inputN, inputM, GL_RGBA, GL_FLOAT, pixelData);
glPixelZoom(1,1);
PsychFlushGL(windowRecord); // This does nothing if we are multi buffered, otherwise it glFlushes
PsychTestForGLErrors();
return PsychError_none;
}
|