// load a single glyph static void LoadGlyph(GFont2D& Font, const FT_Face Face, const GInt32 GlyphIndex, const GReal Scale) { GInt32 loadFlags; GFontChar2D *c; GGlyphMetrics tmpMetrics; FT_Error error; GInt32 i, j, k0, k1, numVert; FT_SubGlyphRec subGlyph; GPoint2 p; GReal x, y; GVect<GReal, 2> v; #define Fixed1616ToReal(a) ((GReal)a / (GReal)65536) // we ignore transformation, scaling and we specify to not recursively load subglyphs loadFlags = FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_RECURSE | FT_LOAD_LINEAR_DESIGN; error = FT_Load_Glyph(Face, GlyphIndex, loadFlags); if (error) return; // check for a good vector format if ((Face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) && (Face->glyph->format != FT_GLYPH_FORMAT_PLOTTER) && (Face->glyph->format != FT_GLYPH_FORMAT_COMPOSITE)) return; // composite glyph, we have to load subglyphs if (Face->glyph->num_subglyphs > 0) { GDynArray<GSubChar2D> subChars(Face->glyph->num_subglyphs); for (i = 0; i < (GInt32)Face->glyph->num_subglyphs; i++) { subGlyph = Face->glyph->subglyphs[i]; subChars[i].GlyphIndex = subGlyph.index; subChars[i].Flags = subGlyph.flags; // rotation and scale subChars[i].Transformation[G_X][G_X] = Fixed1616ToReal(subGlyph.transform.xx); subChars[i].Transformation[G_X][G_Y] = Fixed1616ToReal(subGlyph.transform.xy); // x position subChars[i].Transformation[G_X][G_Z] = subGlyph.arg1 * Scale; // rotation and scale subChars[i].Transformation[G_Y][G_X] = Fixed1616ToReal(subGlyph.transform.yx); subChars[i].Transformation[G_Y][G_Y] = Fixed1616ToReal(subGlyph.transform.yy); // y position subChars[i].Transformation[G_Y][G_Z] = subGlyph.arg2 * Scale; // last row is an identity subChars[i].Transformation[G_Z][G_X] = 0; subChars[i].Transformation[G_Z][G_Y] = 0; subChars[i].Transformation[G_Z][G_Z] = 1; } c = Font.AddChar(subChars); } // simple glyph, lets load contours else { GDynArray<GPoint2> tmpPoints; GDynArray<GInt32> tmpPointsFlags; GDynArray<GFontCharContour2D> tmpContours; k0 = 0; for (i = 0; i < Face->glyph->outline.n_contours; i++) { k1 = Face->glyph->outline.contours[i]; // take into account number of vertices numVert = k1 - k0 + 1; // we discard a bad contour if (numVert < 3) continue; tmpPoints.resize(numVert); tmpPointsFlags.resize(numVert); // read i-th segment for (j = k0; j <= k1; j++) { x = (GReal)Face->glyph->outline.points[j].x; y = (GReal)Face->glyph->outline.points[j].y; tmpPoints[j - k0].Set(x * Scale, y * Scale); tmpPointsFlags[j - k0] = Face->glyph->outline.tags[j]; } // create new contour class GFontCharContour2D newContour(tmpPoints, tmpPointsFlags); // if there was some 0-length segments, they are not inserted; if at the end we have // a bad contour, we can discard it if (newContour.PointsCount() >= 3) tmpContours.push_back(newContour); // next segment k0 = k1 + 1; } c = Font.AddChar(tmpContours); } if (c) { // copy metrics tmpMetrics.Height = (GReal)Face->glyph->metrics.height * Scale; tmpMetrics.Width = (GReal)Face->glyph->metrics.width * Scale; tmpMetrics.HoriAdvance = (GReal)Face->glyph->metrics.horiAdvance * Scale; tmpMetrics.HoriBearingX = (GReal)Face->glyph->metrics.horiBearingX * Scale; tmpMetrics.HoriBearingY = (GReal)Face->glyph->metrics.horiBearingY * Scale; tmpMetrics.VertAdvance = (GReal)Face->glyph->metrics.vertAdvance * Scale; tmpMetrics.VertBearingX = (GReal)Face->glyph->metrics.vertBearingX * Scale; tmpMetrics.VertBearingY = (GReal)Face->glyph->metrics.vertBearingY * Scale; c->SetMetrics(tmpMetrics); // load some basic informations c->SetLinearHoriAdvance((GReal)Face->glyph->linearHoriAdvance * Scale); c->SetLinearVertAdvance((GReal)Face->glyph->linearVertAdvance * Scale); v.Set((GReal)Face->glyph->advance.x * Scale, (GReal)Face->glyph->advance.y * Scale); c->SetAdvance(v); c->SetLSBDelta((GReal)Face->glyph->lsb_delta * Scale); c->SetRSBDelta((GReal)Face->glyph->rsb_delta * Scale); c->SetEvenOddFill((Face->glyph->outline.flags & FT_OUTLINE_EVEN_ODD_FILL) != 0); } }
/** - FUNCIÓ: ComponentLabeling - FUNCIONALITAT: Calcula els components binaris (blobs) d'una imatge amb connectivitat a 8 - PARÀMETRES: - inputImage: image to segment (pixel values different than blobColor are treated as background) - maskImage: if not NULL, all the pixels equal to 0 in mask are skipped in input image - backgroundColor: color of background (ignored pixels) - blobs: blob vector destination - RESULTAT: - - RESTRICCIONS: - - AUTOR: rborras - DATA DE CREACIÓ: 2008/04/21 - MODIFICACIÓ: Data. Autor. Descripció. - NOTA: Algorithm based on "A linear-time component labeling algorithm using contour tracing technique", F.Chang et al */ bool ComponentLabeling( IplImage* inputImage, IplImage* maskImage, unsigned char backgroundColor, Blob_vector &blobs ) { int i,j; // row major vector with visited points bool *visitedPoints, *pVisitedPoints, internalContour, externalContour; unsigned char *pInputImage, *pMask, *pAboveInputImage, *pBelowInputImage, *pAboveMask, *pBelowMask; int imageWidth, imageHeight, currentLabel, contourLabel; // row major vector with labelled image t_labelType *labelledImage, *pLabels; //! current blob pointer CBlob *currentBlob; CvSize imageSizes; CvPoint currentPoint; // verify input image if( !CV_IS_IMAGE( inputImage ) ) return false; // verify that input image and mask image has same size if( maskImage ) { if( !CV_IS_IMAGE(maskImage) || maskImage->width != inputImage->width || maskImage->height != inputImage->height ) return false; } else { pMask = NULL; pAboveMask = NULL; pBelowMask = NULL; } imageSizes = cvSize(inputImage->width,inputImage->height); imageWidth = inputImage->width; imageHeight = inputImage->height; // create auxiliary buffers labelledImage = (t_labelType*) malloc( inputImage->width * inputImage->height * sizeof(t_labelType) ); visitedPoints = (bool*) malloc( inputImage->width * inputImage->height * sizeof(bool) ); // initialize it to 0 memset(labelledImage, 0, inputImage->width * inputImage->height * sizeof(t_labelType) ) ; memset(visitedPoints, false, inputImage->width * inputImage->height * sizeof(bool) ) ; // initialize pointers and label counter pLabels = labelledImage; pVisitedPoints = visitedPoints; currentLabel = 1; for (j = 0; j < imageHeight; j++ ) { // don't verify if we area on first or last row, it will verified on pointer access pAboveInputImage = (unsigned char*) inputImage->imageData + (j-1) * inputImage->widthStep; pBelowInputImage = (unsigned char*) inputImage->imageData + (j+1) * inputImage->widthStep; pInputImage = (unsigned char*) inputImage->imageData + j * inputImage->widthStep; if( maskImage ) { pMask = (unsigned char*) maskImage->imageData + j * maskImage->widthStep; // don't verify if we area on first or last row, it will verified on pointer access pAboveMask = (unsigned char*) maskImage->imageData + (j-1) * maskImage->widthStep; pBelowMask = (unsigned char*) maskImage->imageData + (j+1) * maskImage->widthStep; } for (i = 0; i < imageWidth; i++, pInputImage++, pMask++, pAboveInputImage++, pBelowInputImage++, pAboveMask++, pBelowMask++ ) { // ignore background pixels or 0 pixels in mask if ( (*pInputImage == backgroundColor) || (maskImage && *pMask == 0 )) { pLabels++; pVisitedPoints++; continue; } // new external contour: current label == 0 and above pixel is background if( j > 0 ) { externalContour = ((*pAboveInputImage == backgroundColor) || (maskImage && *pAboveMask == 0)) && (*pLabels == 0); } else externalContour = (*pLabels == 0); // new internal contour: below pixel is background and not visited if( !externalContour && j < imageHeight - 1 ) { internalContour = *pBelowInputImage == backgroundColor && !GET_BELOW_VISITEDPIXEL( pVisitedPoints, imageWidth); } else { internalContour = false; } if( externalContour ) { currentPoint = cvPoint(i,j); // assign label to labelled image *pLabels = currentLabel; // create new blob currentBlob = new CBlob(currentLabel, currentPoint, imageSizes ); // contour tracing with currentLabel contourTracing( inputImage, maskImage, currentPoint, labelledImage, visitedPoints, currentLabel, false, backgroundColor, currentBlob->GetExternalContour() ); // add new created blob blobs.push_back(currentBlob); currentLabel++; } else { if( internalContour ) { currentPoint = cvPoint(i,j); if( *pLabels == 0 ) { // take left neightbour value as current if( i > 0 ) contourLabel = *(pLabels - 1); } else { contourLabel = *pLabels; } if(contourLabel>0) { currentBlob = blobs[contourLabel-1]; CBlobContour newContour(currentPoint, currentBlob->GetStorage()); // contour tracing with contourLabel contourTracing( inputImage, maskImage, currentPoint, labelledImage, visitedPoints, contourLabel, true, backgroundColor, &newContour ); currentBlob->AddInternalContour( newContour ); } } // neither internal nor external contour else { // take left neightbour value as current if it is not labelled if( i > 0 && *pLabels == 0 ) *pLabels = *(pLabels - 1); } } pLabels++; pVisitedPoints++; } } // free auxiliary buffers free( labelledImage ); free( visitedPoints ); return true; }
void cff_parseOutline(uint8_t *data, uint32_t len, cff_Index gsubr, cff_Index lsubr, cff_Stack *stack, void *outline, cff_IOutlineBuilder methods, const otfcc_Options *options) { uint16_t gsubr_bias = compute_subr_bias(gsubr.count); uint16_t lsubr_bias = compute_subr_bias(lsubr.count); uint8_t *start = data; uint32_t advance, i, cnt_bezier; cff_Value val; void (*setWidth)(void *context, double width) = methods.setWidth; void (*newContour)(void *context) = methods.newContour; void (*lineTo)(void *context, double x1, double y1) = methods.lineTo; void (*curveTo)(void *context, double x1, double y1, double x2, double y2, double x3, double y3) = methods.curveTo; void (*setHint)(void *context, bool isVertical, double position, double width) = methods.setHint; void (*setMask)(void *context, bool isContourMask, bool *mask) = methods.setMask; double (*getrand)(void *context) = methods.getrand; if (!setWidth) setWidth = callback_nopSetWidth; if (!newContour) newContour = callback_nopNewContour; if (!lineTo) lineTo = callback_nopLineTo; if (!curveTo) curveTo = callback_nopCurveTo; if (!setHint) setHint = callback_nopsetHint; if (!setMask) setMask = callback_nopsetMask; if (!getrand) getrand = callback_nopgetrand; while (start < data + len) { advance = cff_decodeCS2Token(start, &val); switch (val.t) { case CS2_OPERATOR: switch (val.i) { case op_hstem: case op_vstem: case op_hstemhm: case op_vstemhm: if (stack->index % 2) setWidth(outline, stack->stack[0].d); stack->stem += stack->index >> 1; double hintBase = 0; for (uint16_t j = stack->index % 2; j < stack->index; j += 2) { double pos = stack->stack[j].d; double width = stack->stack[j + 1].d; setHint(outline, (val.i == op_vstem || val.i == op_vstemhm), pos + hintBase, width); hintBase += pos + width; } stack->index = 0; break; case op_hintmask: case op_cntrmask: { if (stack->index % 2) setWidth(outline, stack->stack[0].d); bool isVertical = stack->stem > 0; stack->stem += stack->index >> 1; double hintBase = 0; for (uint16_t j = stack->index % 2; j < stack->index; j += 2) { double pos = stack->stack[j].d; double width = stack->stack[j + 1].d; setHint(outline, isVertical, pos + hintBase, width); hintBase += pos + width; } uint32_t maskLength = (stack->stem + 7) >> 3; bool *mask; NEW(mask, stack->stem + 7); for (uint32_t byte = 0; byte < maskLength; byte++) { uint8_t maskByte = start[advance + byte]; mask[(byte << 3) + 0] = maskByte >> 7 & 1; mask[(byte << 3) + 1] = maskByte >> 6 & 1; mask[(byte << 3) + 2] = maskByte >> 5 & 1; mask[(byte << 3) + 3] = maskByte >> 4 & 1; mask[(byte << 3) + 4] = maskByte >> 3 & 1; mask[(byte << 3) + 5] = maskByte >> 2 & 1; mask[(byte << 3) + 6] = maskByte >> 1 & 1; mask[(byte << 3) + 7] = maskByte >> 0 & 1; } setMask(outline, (val.i == op_cntrmask), mask); advance += maskLength; stack->index = 0; break; } case op_vmoveto: { CHECK_STACK_TOP(op_vmoveto, 1); if (stack->index > 1) setWidth(outline, stack->stack[stack->index - 2].d); newContour(outline); lineTo(outline, 0.0, stack->stack[stack->index - 1].d); stack->index = 0; break; } case op_rmoveto: { CHECK_STACK_TOP(op_rmoveto, 2); if (stack->index > 2) setWidth(outline, stack->stack[stack->index - 3].d); newContour(outline); lineTo(outline, stack->stack[stack->index - 2].d, stack->stack[stack->index - 1].d); stack->index = 0; break; } case op_hmoveto: { CHECK_STACK_TOP(op_hmoveto, 1); if (stack->index > 1) setWidth(outline, stack->stack[stack->index - 2].d); newContour(outline); lineTo(outline, stack->stack[stack->index - 1].d, 0.0); stack->index = 0; break; } case op_endchar: { if (stack->index > 0) setWidth(outline, stack->stack[stack->index - 1].d); break; } case op_rlineto: { for (i = 0; i < stack->index; i += 2) lineTo(outline, stack->stack[i].d, stack->stack[i + 1].d); stack->index = 0; break; } case op_vlineto: { if (stack->index % 2 == 1) { lineTo(outline, 0.0, stack->stack[0].d); for (i = 1; i < stack->index; i += 2) { lineTo(outline, stack->stack[i].d, 0.0); lineTo(outline, 0.0, stack->stack[i + 1].d); } } else { for (i = 0; i < stack->index; i += 2) { lineTo(outline, 0.0, stack->stack[i].d); lineTo(outline, stack->stack[i + 1].d, 0.0); } } stack->index = 0; break; } case op_hlineto: { if (stack->index % 2 == 1) { lineTo(outline, stack->stack[0].d, 0.0); for (i = 1; i < stack->index; i += 2) { lineTo(outline, 0.0, stack->stack[i].d); lineTo(outline, stack->stack[i + 1].d, 0.0); } } else { for (i = 0; i < stack->index; i += 2) { lineTo(outline, stack->stack[i].d, 0.0); lineTo(outline, 0.0, stack->stack[i + 1].d); } } stack->index = 0; break; } case op_rrcurveto: { for (i = 0; i < stack->index; i += 6) curveTo(outline, stack->stack[i].d, stack->stack[i + 1].d, stack->stack[i + 2].d, stack->stack[i + 3].d, stack->stack[i + 4].d, stack->stack[i + 5].d); stack->index = 0; break; } case op_rcurveline: { for (i = 0; i < stack->index - 2; i += 6) curveTo(outline, stack->stack[i].d, stack->stack[i + 1].d, stack->stack[i + 2].d, stack->stack[i + 3].d, stack->stack[i + 4].d, stack->stack[i + 5].d); lineTo(outline, stack->stack[stack->index - 2].d, stack->stack[stack->index - 1].d); stack->index = 0; break; } case op_rlinecurve: { for (i = 0; i < stack->index - 6; i += 2) lineTo(outline, stack->stack[i].d, stack->stack[i + 1].d); curveTo(outline, stack->stack[stack->index - 6].d, stack->stack[stack->index - 5].d, stack->stack[stack->index - 4].d, stack->stack[stack->index - 3].d, stack->stack[stack->index - 2].d, stack->stack[stack->index - 1].d); stack->index = 0; break; } case op_vvcurveto: { if (stack->index % 4 == 1) { curveTo(outline, stack->stack[0].d, stack->stack[1].d, stack->stack[2].d, stack->stack[3].d, 0.0, stack->stack[4].d); for (i = 5; i < stack->index; i += 4) curveTo(outline, 0.0, stack->stack[i].d, stack->stack[i + 1].d, stack->stack[i + 2].d, 0.0, stack->stack[i + 3].d); } else { for (i = 0; i < stack->index; i += 4) curveTo(outline, 0.0, stack->stack[i].d, stack->stack[i + 1].d, stack->stack[i + 2].d, 0.0, stack->stack[i + 3].d); } stack->index = 0; break; } case op_hhcurveto: { if (stack->index % 4 == 1) { curveTo(outline, stack->stack[1].d, stack->stack[0].d, stack->stack[2].d, stack->stack[3].d, stack->stack[4].d, 0.0); for (i = 5; i < stack->index; i += 4) curveTo(outline, stack->stack[i].d, 0.0, stack->stack[i + 1].d, stack->stack[i + 2].d, stack->stack[i + 3].d, 0.0); } else { for (i = 0; i < stack->index; i += 4) curveTo(outline, stack->stack[i].d, 0.0, stack->stack[i + 1].d, stack->stack[i + 2].d, stack->stack[i + 3].d, 0.0); } stack->index = 0; break; } case op_vhcurveto: { if (stack->index % 4 == 1) cnt_bezier = (stack->index - 5) / 4; else cnt_bezier = (stack->index / 4); for (i = 0; i < 4 * cnt_bezier; i += 4) { if ((i / 4) % 2 == 0) curveTo(outline, 0.0, stack->stack[i].d, stack->stack[i + 1].d, stack->stack[i + 2].d, stack->stack[i + 3].d, 0.0); else curveTo(outline, stack->stack[i].d, 0.0, stack->stack[i + 1].d, stack->stack[i + 2].d, 0.0, stack->stack[i + 3].d); } if (stack->index % 8 == 5) { curveTo(outline, 0.0, stack->stack[stack->index - 5].d, stack->stack[stack->index - 4].d, stack->stack[stack->index - 3].d, stack->stack[stack->index - 2].d, stack->stack[stack->index - 1].d); } if (stack->index % 8 == 1) { curveTo(outline, stack->stack[stack->index - 5].d, 0.0, stack->stack[stack->index - 4].d, stack->stack[stack->index - 3].d, stack->stack[stack->index - 1].d, stack->stack[stack->index - 2].d); } stack->index = 0; break; } case op_hvcurveto: { if (stack->index % 4 == 1) cnt_bezier = (stack->index - 5) / 4; else cnt_bezier = (stack->index / 4); for (i = 0; i < 4 * cnt_bezier; i += 4) { if ((i / 4) % 2 == 0) curveTo(outline, stack->stack[i].d, 0.0, stack->stack[i + 1].d, stack->stack[i + 2].d, 0.0, stack->stack[i + 3].d); else curveTo(outline, 0.0, stack->stack[i].d, stack->stack[i + 1].d, stack->stack[i + 2].d, stack->stack[i + 3].d, 0.0); } if (stack->index % 8 == 5) { curveTo(outline, stack->stack[stack->index - 5].d, 0.0, stack->stack[stack->index - 4].d, stack->stack[stack->index - 3].d, stack->stack[stack->index - 1].d, stack->stack[stack->index - 2].d); } if (stack->index % 8 == 1) { curveTo(outline, 0.0, stack->stack[stack->index - 5].d, stack->stack[stack->index - 4].d, stack->stack[stack->index - 3].d, stack->stack[stack->index - 2].d, stack->stack[stack->index - 1].d); } stack->index = 0; break; } case op_hflex: { CHECK_STACK_TOP(op_hflex, 7); curveTo(outline, stack->stack[0].d, 0.0, stack->stack[1].d, stack->stack[2].d, stack->stack[3].d, 0.0); curveTo(outline, stack->stack[4].d, 0.0, stack->stack[5].d, -stack->stack[2].d, stack->stack[6].d, 0.0); stack->index = 0; break; } case op_flex: { CHECK_STACK_TOP(op_flex, 12); curveTo(outline, stack->stack[0].d, stack->stack[1].d, stack->stack[2].d, stack->stack[3].d, stack->stack[4].d, stack->stack[5].d); curveTo(outline, stack->stack[6].d, stack->stack[7].d, stack->stack[8].d, stack->stack[9].d, stack->stack[10].d, stack->stack[11].d); stack->index = 0; break; } case op_hflex1: { CHECK_STACK_TOP(op_hflex1, 9); curveTo(outline, stack->stack[0].d, stack->stack[1].d, stack->stack[2].d, stack->stack[3].d, stack->stack[4].d, 0.0); curveTo(outline, stack->stack[5].d, 0.0, stack->stack[6].d, stack->stack[7].d, stack->stack[8].d, -(stack->stack[1].d + stack->stack[3].d + stack->stack[7].d)); stack->index = 0; break; } case op_flex1: { CHECK_STACK_TOP(op_flex1, 11); double dx = stack->stack[0].d + stack->stack[2].d + stack->stack[4].d + stack->stack[6].d + stack->stack[8].d; double dy = stack->stack[1].d + stack->stack[3].d + stack->stack[5].d + stack->stack[7].d + stack->stack[9].d; if (fabs(dx) > fabs(dy)) { dx = stack->stack[10].d; dy = -dy; } else { dx = -dx; dy = stack->stack[10].d; } curveTo(outline, stack->stack[0].d, stack->stack[1].d, stack->stack[2].d, stack->stack[3].d, stack->stack[4].d, stack->stack[5].d); curveTo(outline, stack->stack[6].d, stack->stack[7].d, stack->stack[8].d, stack->stack[9].d, dx, dy); stack->index = 0; break; } case op_and: { CHECK_STACK_TOP(op_and, 2); double num1 = stack->stack[stack->index - 1].d; double num2 = stack->stack[stack->index - 2].d; stack->stack[stack->index - 2].d = (num1 && num2) ? 1.0 : 0.0; stack->index -= 1; break; } case op_or: { CHECK_STACK_TOP(op_or, 2); double num1 = stack->stack[stack->index - 1].d; double num2 = stack->stack[stack->index - 2].d; stack->stack[stack->index - 2].d = (num1 || num2) ? 1.0 : 0.0; stack->index -= 1; break; } case op_not: { CHECK_STACK_TOP(op_not, 1); double num = stack->stack[stack->index - 1].d; stack->stack[stack->index - 1].d = num ? 0.0 : 1.0; break; } case op_abs: { CHECK_STACK_TOP(op_abs, 1); double num = stack->stack[stack->index - 1].d; stack->stack[stack->index - 1].d = (num < 0.0) ? -num : num; break; } case op_add: { CHECK_STACK_TOP(op_add, 2); double num1 = stack->stack[stack->index - 1].d; double num2 = stack->stack[stack->index - 2].d; stack->stack[stack->index - 2].d = num1 + num2; stack->index -= 1; break; } case op_sub: { CHECK_STACK_TOP(op_sub, 2); double num1 = stack->stack[stack->index - 2].d; double num2 = stack->stack[stack->index - 1].d; stack->stack[stack->index - 2].d = num1 - num2; stack->index -= 1; break; } case op_div: { CHECK_STACK_TOP(op_div, 2); double num1 = stack->stack[stack->index - 2].d; double num2 = stack->stack[stack->index - 1].d; stack->stack[stack->index - 2].d = num1 / num2; stack->index -= 1; break; } case op_neg: { CHECK_STACK_TOP(op_neg, 1); double num = stack->stack[stack->index - 1].d; stack->stack[stack->index - 1].d = -num; break; } case op_eq: { CHECK_STACK_TOP(op_eq, 2); double num1 = stack->stack[stack->index - 1].d; double num2 = stack->stack[stack->index - 2].d; stack->stack[stack->index - 2].d = (num1 == num2) ? 1.0 : 0.0; stack->index -= 1; break; } case op_drop: { CHECK_STACK_TOP(op_drop, 1); stack->index -= 1; break; } case op_put: { CHECK_STACK_TOP(op_put, 2); double val = stack->stack[stack->index - 2].d; int32_t i = (int32_t)stack->stack[stack->index - 1].d; stack->transient[i % type2_transient_array].d = val; stack->index -= 2; break; } case op_get: { CHECK_STACK_TOP(op_get, 1); int32_t i = (int32_t)stack->stack[stack->index - 1].d; stack->stack[stack->index - 1].d = stack->transient[i % type2_transient_array].d; break; } case op_ifelse: { CHECK_STACK_TOP(op_ifelse, 4); double v2 = stack->stack[stack->index - 1].d; double v1 = stack->stack[stack->index - 2].d; double s2 = stack->stack[stack->index - 3].d; double s1 = stack->stack[stack->index - 4].d; stack->stack[stack->index - 4].d = (v1 <= v2) ? s1 : s2; stack->index -= 3; break; } case op_random: { // Chosen from a fair dice // TODO: use a real randomizer stack->stack[stack->index].t = cff_DOUBLE; stack->stack[stack->index].d = getrand(outline); stack->index += 1; break; } case op_mul: { CHECK_STACK_TOP(op_mul, 2); double num1 = stack->stack[stack->index - 1].d; double num2 = stack->stack[stack->index - 2].d; stack->stack[stack->index - 2].d = num1 * num2; stack->index -= 1; break; } case op_sqrt: { CHECK_STACK_TOP(op_sqrt, 1); double num = stack->stack[stack->index - 1].d; stack->stack[stack->index - 1].d = sqrt(num); break; } case op_dup: { CHECK_STACK_TOP(op_dup, 1); stack->stack[stack->index] = stack->stack[stack->index - 1]; stack->index += 1; break; } case op_exch: { CHECK_STACK_TOP(op_exch, 2); double num1 = stack->stack[stack->index - 1].d; double num2 = stack->stack[stack->index - 2].d; stack->stack[stack->index - 1].d = num2; stack->stack[stack->index - 2].d = num1; break; } case op_index: { CHECK_STACK_TOP(op_index, 2); uint8_t n = stack->index - 1; uint8_t j = n - 1 - (uint8_t)(stack->stack[n].d) % n; stack->stack[n] = stack->stack[j]; break; } case op_roll: { CHECK_STACK_TOP(op_roll, 2); int32_t j = stack->stack[stack->index - 1].d; uint32_t n = stack->stack[stack->index - 2].d; CHECK_STACK_TOP(op_roll, 2 + n); j = -j % n; if (j < 0) j += n; if (!j) break; uint8_t last = stack->index - 3; uint8_t first = stack->index - 2 - n; reverseStack(stack, first, last); reverseStack(stack, last - j + 1, last); reverseStack(stack, first, last - j); stack->index -= 2; break; } case op_return: return; case op_callsubr: { CHECK_STACK_TOP(op_callsubr, 1); uint32_t subr = (uint32_t)stack->stack[--(stack->index)].d; cff_parseOutline(lsubr.data + lsubr.offset[lsubr_bias + subr] - 1, lsubr.offset[lsubr_bias + subr + 1] - lsubr.offset[lsubr_bias + subr], gsubr, lsubr, stack, outline, methods, options); break; } case op_callgsubr: { CHECK_STACK_TOP(op_callgsubr, 1); uint32_t subr = (uint32_t)stack->stack[--(stack->index)].d; cff_parseOutline(gsubr.data + gsubr.offset[gsubr_bias + subr] - 1, gsubr.offset[gsubr_bias + subr + 1] - gsubr.offset[gsubr_bias + subr], gsubr, lsubr, stack, outline, methods, options); break; } } break; case CS2_OPERAND: case CS2_FRACTION: stack->stack[(stack->index)++] = val; break; } start += advance; } }