hostsupport/hostopenvg/src/src/riVGU.cpp
branchbug235_bringup_0
changeset 54 067180f57b12
parent 53 c2ef9095503a
child 55 09263774e342
equal deleted inserted replaced
53:c2ef9095503a 54:067180f57b12
     1 /*------------------------------------------------------------------------
       
     2  *
       
     3  * VGU library for OpenVG 1.1 Reference Implementation
       
     4  * ---------------------------------------------------
       
     5  *
       
     6  * Copyright (c) 2007 The Khronos Group Inc.
       
     7  * Portions copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     8  *
       
     9  * Permission is hereby granted, free of charge, to any person obtaining a
       
    10  * copy of this software and /or associated documentation files
       
    11  * (the "Materials "), to deal in the Materials without restriction,
       
    12  * including without limitation the rights to use, copy, modify, merge,
       
    13  * publish, distribute, sublicense, and/or sell copies of the Materials,
       
    14  * and to permit persons to whom the Materials are furnished to do so,
       
    15  * subject to the following conditions: 
       
    16  *
       
    17  * The above copyright notice and this permission notice shall be included 
       
    18  * in all copies or substantial portions of the Materials. 
       
    19  *
       
    20  * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       
    21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       
    22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
       
    23  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
       
    24  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
       
    25  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR
       
    26  * THE USE OR OTHER DEALINGS IN THE MATERIALS.
       
    27  *
       
    28  *//**
       
    29  * \file
       
    30  * \brief	Implementation of the VGU utility library for OpenVG
       
    31  *//*-------------------------------------------------------------------*/
       
    32 
       
    33 #include "vgu.h"
       
    34 #include "openvg.h"
       
    35 #include "riMath.h"
       
    36 
       
    37 using namespace OpenVGRI;
       
    38 
       
    39 /*-------------------------------------------------------------------*//*!
       
    40 * \brief	
       
    41 * \param	
       
    42 * \return	
       
    43 * \note		
       
    44 *//*-------------------------------------------------------------------*/
       
    45 
       
    46 static void append(VGPath path, int numSegments, const VGubyte* segments, int numCoordinates, const VGfloat* coordinates)
       
    47 {
       
    48 	RI_ASSERT(numCoordinates <= 26);
       
    49 
       
    50 	VGPathDatatype datatype = (VGPathDatatype)vgGetParameteri(path, VG_PATH_DATATYPE);
       
    51 	VGfloat scale = vgGetParameterf(path, VG_PATH_SCALE);
       
    52 	VGfloat bias = vgGetParameterf(path, VG_PATH_BIAS);
       
    53 
       
    54 	switch(datatype)
       
    55 	{
       
    56 	case VG_PATH_DATATYPE_S_8:
       
    57 	{
       
    58 		RIint8 data[26];
       
    59 		for(int i=0;i<numCoordinates;i++)
       
    60 			data[i] = (RIint8)floor((coordinates[i] - bias) / scale + 0.5f);	//add 0.5 for correct rounding
       
    61 		vgAppendPathData(path, numSegments, segments, data);
       
    62 		break;
       
    63 	}
       
    64 
       
    65 	case VG_PATH_DATATYPE_S_16:
       
    66 	{
       
    67 		RIint16 data[26];
       
    68 		for(int i=0;i<numCoordinates;i++)
       
    69 			data[i] = (RIint16)floor((coordinates[i] - bias) / scale + 0.5f);	//add 0.5 for correct rounding
       
    70 		vgAppendPathData(path, numSegments, segments, data);
       
    71 		break;
       
    72 	}
       
    73 
       
    74 	case VG_PATH_DATATYPE_S_32:
       
    75 	{
       
    76 		RIint32 data[26];
       
    77 		for(int i=0;i<numCoordinates;i++)
       
    78 			data[i] = (RIint32)floor((coordinates[i] - bias) / scale + 0.5f);	//add 0.5 for correct rounding
       
    79 		vgAppendPathData(path, numSegments, segments, data);
       
    80 		break;
       
    81 	}
       
    82 
       
    83 	default:
       
    84 	{
       
    85 		RI_ASSERT(datatype == VG_PATH_DATATYPE_F);
       
    86 		RIfloat32 data[26];
       
    87 		for(int i=0;i<numCoordinates;i++)
       
    88 			data[i] = (RIfloat32)((coordinates[i] - bias) / scale);
       
    89 		vgAppendPathData(path, numSegments, segments, data);
       
    90 		break;
       
    91 	}
       
    92 	}
       
    93 }
       
    94 
       
    95 /*-------------------------------------------------------------------*//*!
       
    96 * \brief	
       
    97 * \param	
       
    98 * \return	
       
    99 * \note		
       
   100 *//*-------------------------------------------------------------------*/
       
   101 
       
   102 VGUErrorCode RI_APIENTRY vguLine(VGPath path, VGfloat x0, VGfloat y0, VGfloat x1, VGfloat y1)
       
   103 {
       
   104 	VGErrorCode error = vgGetError();	//clear the error state
       
   105 	static const VGubyte segments[2] = {VG_MOVE_TO | VG_ABSOLUTE, VG_LINE_TO | VG_ABSOLUTE};
       
   106 	const VGfloat data[4] = {x0, y0, x1, y1};
       
   107 	append(path, 2, segments, 4, data);
       
   108 
       
   109 	error = vgGetError();
       
   110 	if(error == VG_BAD_HANDLE_ERROR)
       
   111 		return VGU_BAD_HANDLE_ERROR;
       
   112 	else if(error == VG_PATH_CAPABILITY_ERROR)
       
   113 		return VGU_PATH_CAPABILITY_ERROR;
       
   114 	return VGU_NO_ERROR;
       
   115 }
       
   116 
       
   117 /*-------------------------------------------------------------------*//*!
       
   118 * \brief	
       
   119 * \param	
       
   120 * \return	
       
   121 * \note		
       
   122 *//*-------------------------------------------------------------------*/
       
   123 
       
   124 VGUErrorCode RI_APIENTRY vguPolygon(VGPath path, const VGfloat * points, VGint count, VGboolean closed)
       
   125 {
       
   126 	VGErrorCode error = vgGetError();	//clear the error state
       
   127 	if(!points || (((RIuintptr)points) & 3) || count <= 0)
       
   128 		return VGU_ILLEGAL_ARGUMENT_ERROR;
       
   129 
       
   130 	VGubyte segments[1] = {VG_MOVE_TO | VG_ABSOLUTE};
       
   131 	VGfloat data[2];
       
   132 	for(int i=0;i<count;i++)
       
   133 	{
       
   134 		data[0] = points[i*2+0];
       
   135 		data[1] = points[i*2+1];
       
   136 		append(path, 1, segments, 2, data);
       
   137 		segments[0] = VG_LINE_TO | VG_ABSOLUTE;
       
   138 	}
       
   139 	if(closed)
       
   140 	{
       
   141 		segments[0] = VG_CLOSE_PATH;
       
   142 		append(path, 1, segments, 0, data);
       
   143 	}
       
   144 
       
   145 	error = vgGetError();
       
   146 	if(error == VG_BAD_HANDLE_ERROR)
       
   147 		return VGU_BAD_HANDLE_ERROR;
       
   148 	else if(error == VG_PATH_CAPABILITY_ERROR)
       
   149 		return VGU_PATH_CAPABILITY_ERROR;
       
   150 	return VGU_NO_ERROR;
       
   151 }
       
   152 
       
   153 /*-------------------------------------------------------------------*//*!
       
   154 * \brief	
       
   155 * \param	
       
   156 * \return	
       
   157 * \note		
       
   158 *//*-------------------------------------------------------------------*/
       
   159 
       
   160 VGUErrorCode RI_APIENTRY vguRect(VGPath path, VGfloat x, VGfloat y, VGfloat width, VGfloat height)
       
   161 {
       
   162 	VGErrorCode error = vgGetError();	//clear the error state
       
   163 	if(width <= 0 || height <= 0)
       
   164 		return VGU_ILLEGAL_ARGUMENT_ERROR;
       
   165 
       
   166 	static const VGubyte segments[5] = {VG_MOVE_TO | VG_ABSOLUTE,
       
   167 										VG_HLINE_TO | VG_ABSOLUTE,
       
   168 										VG_VLINE_TO | VG_ABSOLUTE,
       
   169 										VG_HLINE_TO | VG_ABSOLUTE,
       
   170 										VG_CLOSE_PATH};
       
   171 	const VGfloat data[5] = {x, y, x + width, y + height, x};
       
   172 	append(path, 5, segments, 5, data);
       
   173 
       
   174 	error = vgGetError();
       
   175 	if(error == VG_BAD_HANDLE_ERROR)
       
   176 		return VGU_BAD_HANDLE_ERROR;
       
   177 	else if(error == VG_PATH_CAPABILITY_ERROR)
       
   178 		return VGU_PATH_CAPABILITY_ERROR;
       
   179 	return VGU_NO_ERROR;
       
   180 }
       
   181 
       
   182 /*-------------------------------------------------------------------*//*!
       
   183 * \brief	
       
   184 * \param	
       
   185 * \return	
       
   186 * \note		
       
   187 *//*-------------------------------------------------------------------*/
       
   188 
       
   189 VGUErrorCode RI_APIENTRY vguRoundRect(VGPath path, VGfloat x, VGfloat y, VGfloat width, VGfloat height, VGfloat arcWidth, VGfloat arcHeight)
       
   190 {
       
   191 	VGErrorCode error = vgGetError();	//clear the error state
       
   192 	if(width <= 0 || height <= 0)
       
   193 		return VGU_ILLEGAL_ARGUMENT_ERROR;
       
   194 
       
   195 	arcWidth = RI_CLAMP(arcWidth, 0.0f, width);
       
   196 	arcHeight = RI_CLAMP(arcHeight, 0.0f, height);
       
   197 
       
   198 	static const VGubyte segments[10] = {VG_MOVE_TO | VG_ABSOLUTE,
       
   199 										 VG_HLINE_TO | VG_ABSOLUTE,
       
   200 										 VG_SCCWARC_TO | VG_ABSOLUTE,
       
   201 										 VG_VLINE_TO | VG_ABSOLUTE,
       
   202 										 VG_SCCWARC_TO | VG_ABSOLUTE,
       
   203 										 VG_HLINE_TO | VG_ABSOLUTE,
       
   204 										 VG_SCCWARC_TO | VG_ABSOLUTE,
       
   205 										 VG_VLINE_TO | VG_ABSOLUTE,
       
   206 										 VG_SCCWARC_TO | VG_ABSOLUTE,
       
   207 										 VG_CLOSE_PATH};
       
   208 	const VGfloat data[26] = {x + arcWidth/2, y,
       
   209 							  x + width - arcWidth/2,
       
   210 							  arcWidth/2, arcHeight/2, 0, x + width, y + arcHeight/2,
       
   211 							  y + height - arcHeight/2,
       
   212 							  arcWidth/2, arcHeight/2, 0, x + width - arcWidth/2, y + height,
       
   213 							  x + arcWidth/2,
       
   214 							  arcWidth/2, arcHeight/2, 0, x, y + height - arcHeight/2,
       
   215 							  y + arcHeight/2,
       
   216 							  arcWidth/2, arcHeight/2, 0, x + arcWidth/2, y};
       
   217 	append(path, 10, segments, 26, data);
       
   218 
       
   219 	error = vgGetError();
       
   220 	if(error == VG_BAD_HANDLE_ERROR)
       
   221 		return VGU_BAD_HANDLE_ERROR;
       
   222 	else if(error == VG_PATH_CAPABILITY_ERROR)
       
   223 		return VGU_PATH_CAPABILITY_ERROR;
       
   224 	return VGU_NO_ERROR;
       
   225 }
       
   226 
       
   227 /*-------------------------------------------------------------------*//*!
       
   228 * \brief	
       
   229 * \param	
       
   230 * \return	
       
   231 * \note		
       
   232 *//*-------------------------------------------------------------------*/
       
   233 
       
   234 VGUErrorCode RI_APIENTRY vguEllipse(VGPath path, VGfloat cx, VGfloat cy, VGfloat width, VGfloat height)
       
   235 {
       
   236 	VGErrorCode error = vgGetError();	//clear the error state
       
   237 	if(width <= 0 || height <= 0)
       
   238 		return VGU_ILLEGAL_ARGUMENT_ERROR;
       
   239 
       
   240 	static const VGubyte segments[4] = {VG_MOVE_TO | VG_ABSOLUTE,
       
   241 										VG_SCCWARC_TO | VG_ABSOLUTE,
       
   242 										VG_SCCWARC_TO | VG_ABSOLUTE,
       
   243 										VG_CLOSE_PATH};
       
   244 	const VGfloat data[12] = {cx + width/2, cy,
       
   245 							  width/2, height/2, 0, cx - width/2, cy,
       
   246 							  width/2, height/2, 0, cx + width/2, cy};
       
   247 	append(path, 4, segments, 12, data);
       
   248 
       
   249 	error = vgGetError();
       
   250 	if(error == VG_BAD_HANDLE_ERROR)
       
   251 		return VGU_BAD_HANDLE_ERROR;
       
   252 	else if(error == VG_PATH_CAPABILITY_ERROR)
       
   253 		return VGU_PATH_CAPABILITY_ERROR;
       
   254 	return VGU_NO_ERROR;
       
   255 }
       
   256 
       
   257 /*-------------------------------------------------------------------*//*!
       
   258 * \brief	
       
   259 * \param	
       
   260 * \return	
       
   261 * \note		
       
   262 *//*-------------------------------------------------------------------*/
       
   263 
       
   264 VGUErrorCode RI_APIENTRY vguArc(VGPath path, VGfloat x, VGfloat y, VGfloat width, VGfloat height, VGfloat startAngle, VGfloat angleExtent, VGUArcType arcType)
       
   265 {
       
   266 	VGErrorCode error = vgGetError();	//clear the error state
       
   267 	if((arcType != VGU_ARC_OPEN && arcType != VGU_ARC_CHORD && arcType != VGU_ARC_PIE) || width <= 0.0f || height <= 0.0f)
       
   268 		return VGU_ILLEGAL_ARGUMENT_ERROR;
       
   269 
       
   270 	startAngle = RI_DEG_TO_RAD(startAngle);
       
   271 	angleExtent = RI_DEG_TO_RAD(angleExtent);
       
   272 
       
   273 	VGfloat w = width/2.0f;
       
   274 	VGfloat h = height/2.0f;
       
   275 
       
   276 	VGubyte segments[1];
       
   277 	VGfloat data[5];
       
   278 
       
   279 	segments[0] = VG_MOVE_TO | VG_ABSOLUTE;
       
   280 	data[0] = x + w * (VGfloat)cos(startAngle);
       
   281 	data[1] = y + h * (VGfloat)sin(startAngle);
       
   282 	append(path, 1, segments, 2, data);
       
   283 
       
   284 	data[0] = w;
       
   285 	data[1] = h;
       
   286 	data[2] = 0.0f;
       
   287 	VGfloat endAngle = startAngle + angleExtent;
       
   288 	if(angleExtent >= 0.0f)
       
   289 	{
       
   290 		segments[0] = VG_SCCWARC_TO | VG_ABSOLUTE;
       
   291 		for(VGfloat a = startAngle + RI_PI;a < endAngle; a += RI_PI)
       
   292 		{
       
   293             if ((a + RI_PI) == a)
       
   294                 return VGU_ILLEGAL_ARGUMENT_ERROR;
       
   295 
       
   296 			data[3] = x + w * (VGfloat)cos(a);
       
   297 			data[4] = y + h * (VGfloat)sin(a);
       
   298 			append(path, 1, segments, 5, data);
       
   299 		}
       
   300 	}
       
   301 	else
       
   302 	{
       
   303 		segments[0] = VG_SCWARC_TO | VG_ABSOLUTE;
       
   304 		for(VGfloat a = startAngle - RI_PI;a > endAngle; a -= RI_PI)
       
   305 		{
       
   306             if ((a - RI_PI) == a)
       
   307                 return VGU_ILLEGAL_ARGUMENT_ERROR;
       
   308 
       
   309 			data[3] = x + w * (VGfloat)cos(a);
       
   310 			data[4] = y + h * (VGfloat)sin(a);
       
   311 			append(path, 1, segments, 5, data);
       
   312 		}
       
   313 	}
       
   314 	data[3] = x + w * (VGfloat)cos(endAngle);
       
   315 	data[4] = y + h * (VGfloat)sin(endAngle);
       
   316 	append(path, 1, segments, 5, data);
       
   317 
       
   318 	if(arcType == VGU_ARC_CHORD)
       
   319 	{
       
   320 		segments[0] = VG_CLOSE_PATH;
       
   321 		append(path, 1, segments, 0, data);
       
   322 	}
       
   323 	else if(arcType == VGU_ARC_PIE)
       
   324 	{
       
   325 		segments[0] = VG_LINE_TO | VG_ABSOLUTE;
       
   326 		data[0] = x;
       
   327 		data[1] = y;
       
   328 		append(path, 1, segments, 2, data);
       
   329 		segments[0] = VG_CLOSE_PATH;
       
   330 		append(path, 1, segments, 0, data);
       
   331 	}
       
   332 
       
   333 	error = vgGetError();
       
   334 	if(error == VG_BAD_HANDLE_ERROR)
       
   335 		return VGU_BAD_HANDLE_ERROR;
       
   336 	else if(error == VG_PATH_CAPABILITY_ERROR)
       
   337 		return VGU_PATH_CAPABILITY_ERROR;
       
   338 	return VGU_NO_ERROR;
       
   339 }
       
   340 
       
   341 /*-------------------------------------------------------------------*//*!
       
   342 * \brief	
       
   343 * \param	
       
   344 * \return	
       
   345 * \note		
       
   346 *//*-------------------------------------------------------------------*/
       
   347 
       
   348 VGUErrorCode RI_APIENTRY vguComputeWarpQuadToSquare(VGfloat sx0, VGfloat sy0, VGfloat sx1, VGfloat sy1, VGfloat sx2, VGfloat sy2, VGfloat sx3, VGfloat sy3, VGfloat * matrix)
       
   349 {
       
   350 	if(!matrix || ((RIuintptr)matrix) & 3)
       
   351 		return VGU_ILLEGAL_ARGUMENT_ERROR;
       
   352 
       
   353 	VGfloat mat[9];
       
   354 	VGUErrorCode ret = vguComputeWarpSquareToQuad(sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3, mat);
       
   355 	if(ret == VGU_BAD_WARP_ERROR)
       
   356 		return VGU_BAD_WARP_ERROR;
       
   357 	Matrix3x3 m(mat[0], mat[3], mat[6],
       
   358 				mat[1], mat[4], mat[7],
       
   359 				mat[2], mat[5], mat[8]);
       
   360 	bool nonsingular = m.invert();
       
   361 	if(!nonsingular)
       
   362 		return VGU_BAD_WARP_ERROR;
       
   363 	matrix[0] = m[0][0];
       
   364 	matrix[1] = m[1][0];
       
   365 	matrix[2] = m[2][0];
       
   366 	matrix[3] = m[0][1];
       
   367 	matrix[4] = m[1][1];
       
   368 	matrix[5] = m[2][1];
       
   369 	matrix[6] = m[0][2];
       
   370 	matrix[7] = m[1][2];
       
   371 	matrix[8] = m[2][2];
       
   372 	return VGU_NO_ERROR;
       
   373 }
       
   374 
       
   375 /*-------------------------------------------------------------------*//*!
       
   376 * \brief	
       
   377 * \param	
       
   378 * \return	
       
   379 * \note		
       
   380 *//*-------------------------------------------------------------------*/
       
   381 
       
   382 VGUErrorCode RI_APIENTRY vguComputeWarpSquareToQuad(VGfloat dx0, VGfloat dy0, VGfloat dx1, VGfloat dy1, VGfloat dx2, VGfloat dy2, VGfloat dx3, VGfloat dy3, VGfloat * matrix)
       
   383 {
       
   384 	if(!matrix || ((RIuintptr)matrix) & 3)
       
   385 		return VGU_ILLEGAL_ARGUMENT_ERROR;
       
   386 
       
   387 	//from Heckbert:Fundamentals of Texture Mapping and Image Warping
       
   388 	//Note that his mapping of vertices is different from OpenVG's
       
   389 	//(0,0) => (dx0,dy0)
       
   390 	//(1,0) => (dx1,dy1)
       
   391 	//(0,1) => (dx2,dy2)
       
   392 	//(1,1) => (dx3,dy3)
       
   393 
       
   394 	VGfloat diffx1 = dx1 - dx3;
       
   395 	VGfloat diffy1 = dy1 - dy3;
       
   396 	VGfloat diffx2 = dx2 - dx3;
       
   397 	VGfloat diffy2 = dy2 - dy3;
       
   398 
       
   399 	VGfloat det = diffx1*diffy2 - diffx2*diffy1;
       
   400 	if(det == 0.0f)
       
   401 		return VGU_BAD_WARP_ERROR;
       
   402 
       
   403 	VGfloat sumx = dx0 - dx1 + dx3 - dx2;
       
   404 	VGfloat sumy = dy0 - dy1 + dy3 - dy2;
       
   405 
       
   406 	if(sumx == 0.0f && sumy == 0.0f)
       
   407 	{	//affine mapping
       
   408 		matrix[0] = dx1 - dx0;
       
   409 		matrix[1] = dy1 - dy0;
       
   410 		matrix[2] = 0.0f;
       
   411 		matrix[3] = dx3 - dx1;
       
   412 		matrix[4] = dy3 - dy1;
       
   413 		matrix[5] = 0.0f;
       
   414 		matrix[6] = dx0;
       
   415 		matrix[7] = dy0;
       
   416 		matrix[8] = 1.0f;
       
   417 		return VGU_NO_ERROR;
       
   418 	}
       
   419 
       
   420 	VGfloat oodet = 1.0f / det;
       
   421 	VGfloat g = (sumx*diffy2 - diffx2*sumy) * oodet;
       
   422 	VGfloat h = (diffx1*sumy - sumx*diffy1) * oodet;
       
   423 
       
   424 	matrix[0] = dx1-dx0+g*dx1;
       
   425 	matrix[1] = dy1-dy0+g*dy1;
       
   426 	matrix[2] = g;
       
   427 	matrix[3] = dx2-dx0+h*dx2;
       
   428 	matrix[4] = dy2-dy0+h*dy2;
       
   429 	matrix[5] = h;
       
   430 	matrix[6] = dx0;
       
   431 	matrix[7] = dy0;
       
   432 	matrix[8] = 1.0f;
       
   433 	return VGU_NO_ERROR;
       
   434 }
       
   435 
       
   436 /*-------------------------------------------------------------------*//*!
       
   437 * \brief	
       
   438 * \param	
       
   439 * \return	
       
   440 * \note		
       
   441 *//*-------------------------------------------------------------------*/
       
   442 
       
   443 VGUErrorCode RI_APIENTRY vguComputeWarpQuadToQuad(VGfloat dx0, VGfloat dy0, VGfloat dx1, VGfloat dy1, VGfloat dx2, VGfloat dy2, VGfloat dx3, VGfloat dy3, VGfloat sx0, VGfloat sy0, VGfloat sx1, VGfloat sy1, VGfloat sx2, VGfloat sy2, VGfloat sx3, VGfloat sy3, VGfloat * matrix)
       
   444 {
       
   445 	if(!matrix || ((RIuintptr)matrix) & 3)
       
   446 		return VGU_ILLEGAL_ARGUMENT_ERROR;
       
   447 
       
   448 	VGfloat qtos[9];
       
   449 	VGUErrorCode ret1 = vguComputeWarpQuadToSquare(sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3, qtos);
       
   450 	if(ret1 == VGU_BAD_WARP_ERROR)
       
   451 		return VGU_BAD_WARP_ERROR;
       
   452 
       
   453 	VGfloat stoq[9];
       
   454 	VGUErrorCode ret2 = vguComputeWarpSquareToQuad(dx0, dy0, dx1, dy1, dx2, dy2, dx3, dy3, stoq);
       
   455 	if(ret2 == VGU_BAD_WARP_ERROR)
       
   456 		return VGU_BAD_WARP_ERROR;
       
   457 
       
   458 	Matrix3x3 m1(qtos[0], qtos[3], qtos[6],
       
   459 				 qtos[1], qtos[4], qtos[7],
       
   460 				 qtos[2], qtos[5], qtos[8]);
       
   461 	Matrix3x3 m2(stoq[0], stoq[3], stoq[6],
       
   462 				 stoq[1], stoq[4], stoq[7],
       
   463 				 stoq[2], stoq[5], stoq[8]);
       
   464 	Matrix3x3 r = m2 * m1;
       
   465 
       
   466 	matrix[0] = r[0][0];
       
   467 	matrix[1] = r[1][0];
       
   468 	matrix[2] = r[2][0];
       
   469 	matrix[3] = r[0][1];
       
   470 	matrix[4] = r[1][1];
       
   471 	matrix[5] = r[2][1];
       
   472 	matrix[6] = r[0][2];
       
   473 	matrix[7] = r[1][2];
       
   474 	matrix[8] = r[2][2];
       
   475 	return VGU_NO_ERROR;
       
   476 }