180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru/* 380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * Copyright 2006 The Android Open Source Project 480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * 580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * Use of this source code is governed by a BSD-style license that can be 680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru * found in the LICENSE file. 780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru */ 880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 1080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkDrawPath.h" 1180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkAnimateMaker.h" 1280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkCanvas.h" 1380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkMath.h" 1480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkMatrixParts.h" 1580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkPaint.h" 1680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#include "SkPathParts.h" 1780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 1880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruenum SkPath_Properties { 1980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SK_PROPERTY(fillType), 2080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SK_PROPERTY(length) 2180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}; 2280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 2380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#if SK_USE_CONDENSED_INFO == 0 2480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 2580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruconst SkMemberInfo SkDrawPath::fInfo[] = { 2680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SK_MEMBER(d, String), 2780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SK_MEMBER_PROPERTY(fillType, FillType), 2880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SK_MEMBER_PROPERTY(length, Float) 2980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}; 3080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 3180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#endif 3280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 3380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste QueruDEFINE_GET_MEMBER(SkDrawPath); 3480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 3580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste QueruSkDrawPath::SkDrawPath() 3680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru{ 3780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fParent = NULL; 3880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fLength = SK_ScalarNaN; 3980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fChildHasID = false; 4080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fDirty = false; 4180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 4280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 4380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste QueruSkDrawPath::~SkDrawPath() { 4480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru for (SkPathPart** part = fParts.begin(); part < fParts.end(); part++) 4580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru delete *part; 4680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 4780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 48363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerbool SkDrawPath::addChild(SkAnimateMaker& maker, SkDisplayable* child) { 4980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkASSERT(child && child->isPathPart()); 5080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkPathPart* part = (SkPathPart*) child; 5180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru *fParts.append() = part; 5280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru if (part->add()) 5380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToPath); 5480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fDirty = false; 5580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return true; 5680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 5780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 5880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querubool SkDrawPath::childrenNeedDisposing() const { 5980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return false; 6080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 6180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 6280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruvoid SkDrawPath::dirty() { 6380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fDirty = true; 6480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fLength = SK_ScalarNaN; 6580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru if (fParent) 6680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fParent->dirty(); 6780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 6880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 6980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querubool SkDrawPath::draw(SkAnimateMaker& maker) { 7080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkPath& path = getPath(); 7180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkBoundableAuto boundable(this, maker); 7280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru maker.fCanvas->drawPath(path, *maker.fPaint); 7380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return false; 7480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 7580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 7680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste QueruSkDisplayable* SkDrawPath::getParent() const { 7780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return fParent; 7880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 7980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 8080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#ifdef SK_DUMP_ENABLED 8180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruvoid SkDrawPath::dump(SkAnimateMaker* maker) { 8280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru dumpBase(maker); 8380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru dumpAttrs(maker); 8480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru bool closedYet = false; 8580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkDisplayList::fIndent += 4; 8680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru for(SkPathPart** part = fParts.begin(); part < fParts.end(); part++) { 8780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru if (closedYet == false) { 8880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkDebugf(">\n"); 8980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru closedYet = true; 9080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru } 9180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru (*part)->dump(maker); 9280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru } 9380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkDisplayList::fIndent -= 4; 9480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru if (closedYet) 9580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru dumpEnd(maker); 9680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru else 9780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkDebugf("/>\n"); 9880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 9980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#endif 10080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 10180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste QueruSkPath& SkDrawPath::getPath() { 10280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru if (fDirty == false) 10380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return fPath; 10480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru if (d.size() > 0) 10580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru { 10680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru parseSVG(); 10780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru d.reset(); 10880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru } 10980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru else 11080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru { 11180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fPath.reset(); 11280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru for (SkPathPart** part = fParts.begin(); part < fParts.end(); part++) 11380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru (*part)->add(); 11480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru } 11580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fDirty = false; 11680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return fPath; 11780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 11880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 11980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruvoid SkDrawPath::onEndElement(SkAnimateMaker& ) { 12080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru if (d.size() > 0) { 12180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru parseSVG(); 12280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru d.reset(); 12380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fDirty = false; 12480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return; 12580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru } 12680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru if (fChildHasID == false) { 12780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru for (SkPathPart** part = fParts.begin(); part < fParts.end(); part++) 12880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru delete *part; 12980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fParts.reset(); 13080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fDirty = false; 13180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru } 13280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 13380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 13480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querubool SkDrawPath::getProperty(int index, SkScriptValue* value) const { 13580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru switch (index) { 13680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru case SK_PROPERTY(length): 13780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru if (SkScalarIsNaN(fLength)) { 13880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru const SkPath& path = ((SkDrawPath*) this)->getPath(); 13980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkPathMeasure pathMeasure(path, false); 14080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fLength = pathMeasure.getLength(); 14180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru } 14280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru value->fType = SkType_Float; 14380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru value->fOperand.fScalar = fLength; 14480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru break; 14580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru case SK_PROPERTY(fillType): 14680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru value->fType = SkType_FillType; 14780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru value->fOperand.fS32 = (int) fPath.getFillType(); 14880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru break; 14980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru default: 15080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkASSERT(0); 15180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return false; 15280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru } 15380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return true; 15480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 15580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 15680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruvoid SkDrawPath::setChildHasID() { 15780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fChildHasID = true; 15880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 15980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 16080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querubool SkDrawPath::setParent(SkDisplayable* parent) { 16180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fParent = parent; 16280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return false; 16380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 16480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 16580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Querubool SkDrawPath::setProperty(int index, SkScriptValue& value) 16680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru{ 16780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru switch (index) { 16880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru case SK_PROPERTY(fillType): 16980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkASSERT(value.fType == SkType_FillType); 17080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkASSERT(value.fOperand.fS32 >= SkPath::kWinding_FillType && 17180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru value.fOperand.fS32 <= SkPath::kEvenOdd_FillType); 17280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fPath.setFillType((SkPath::FillType) value.fOperand.fS32); 17380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru break; 17480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru default: 17580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SkASSERT(0); 17680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return false; 17780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru } 17880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return true; 17980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 18080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 18180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#if SK_USE_CONDENSED_INFO == 0 18280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 18380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruconst SkMemberInfo SkPolyline::fInfo[] = { 18480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SK_MEMBER_ARRAY(points, Float) 18580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}; 18680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 18780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#endif 18880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 18980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste QueruDEFINE_GET_MEMBER(SkPolyline); 19080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 191363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerbool SkPolyline::addChild(SkAnimateMaker& , SkDisplayable*) { 19280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return false; 19380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 19480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 19580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruvoid SkPolyline::onEndElement(SkAnimateMaker& maker) { 19680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru INHERITED::onEndElement(maker); 19780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru if (points.count() <= 0) 19880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru return; 19980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fPath.reset(); 20080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fPath.moveTo(points[0], points[1]); 20180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru int count = points.count(); 20280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru for (int index = 2; index < count; index += 2) 20380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fPath.lineTo(points[index], points[index+1]); 20480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 20580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 20680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 20780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#if SK_USE_CONDENSED_INFO == 0 20880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 20980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruconst SkMemberInfo SkPolygon::fInfo[] = { 21080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru SK_MEMBER_INHERITED 21180bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru}; 21280bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 21380bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru#endif 21480bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 21580bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste QueruDEFINE_GET_MEMBER(SkPolygon); 21680bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru 21780bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queruvoid SkPolygon::onEndElement(SkAnimateMaker& maker) { 21880bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru INHERITED::onEndElement(maker); 21980bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru fPath.close(); 22080bacfeb4bda06541e8695bd502229727bccfeaJean-Baptiste Queru} 221