rsFont.h revision d3e0ad43dc758c409fc23d1893dab67b18520c24
1d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk/*
2d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk * Copyright (C) 2009 The Android Open Source Project
3d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk *
4d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk * Licensed under the Apache License, Version 2.0 (the "License");
5d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk * you may not use this file except in compliance with the License.
6d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk * You may obtain a copy of the License at
7d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk *
8d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk *      http://www.apache.org/licenses/LICENSE-2.0
9d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk *
10d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk * Unless required by applicable law or agreed to in writing, software
11d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk * distributed under the License is distributed on an "AS IS" BASIS,
12d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk * See the License for the specific language governing permissions and
14d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk * limitations under the License.
15d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk */
16d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
17d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk#ifndef ANDROID_RS_FONT_H
18d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk#define ANDROID_RS_FONT_H
19d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
20d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk#include "RenderScript.h"
21d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk#include "rsStream.h"
22d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk#include <utils/String8.h>
23d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk#include <utils/Vector.h>
24d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk#include <utils/KeyedVector.h>
25d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
26d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk#include <ft2build.h>
27d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk#include FT_FREETYPE_H
28d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
29d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk// ---------------------------------------------------------------------------
30d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouknamespace android {
31d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
32d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouknamespace renderscript {
33d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
34d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchoukclass FontState;
35d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
36d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchoukclass Font : public ObjectBase
37d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk{
38d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchoukpublic:
39d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    ~Font();
40d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
41d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    // Pointer to the utf data, length of data, where to start, number of glyphs ot read
42d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    // (each glyph may be longer than a char because we are dealing with utf data)
43d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    // Last two variables are the initial pen position
44d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyphs, int x, int y);
45d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
46d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    // Currently files do not get serialized,
47d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    // but we need to inherit from ObjectBase for ref tracking
48d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    virtual void serialize(OStream *stream) const {
49d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    }
50d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    virtual RsA3DClassID getClassId() const {
51d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        return RS_A3D_CLASS_ID_UNKNOWN;
52d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    }
53d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
54d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    static Font * create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi);
55d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
56d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchoukprotected:
57d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
58d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    friend class FontState;
59d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
60d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void invalidateTextureCache();
61d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    struct CachedGlyphInfo
62d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    {
63d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        // Has the cache been invalidated?
64d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        bool mIsValid;
65d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        // Location of the cached glyph in the bitmap
66d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        // in case we need to resize the texture
67d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        uint32_t mBitmapMinX;
68d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        uint32_t mBitmapMinY;
69d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        uint32_t mBitmapWidth;
70d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        uint32_t mBitmapHeight;
71d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        // Also cache texture coords for the quad
72d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        float mBitmapMinU;
73d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        float mBitmapMinV;
74d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        float mBitmapMaxU;
75d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        float mBitmapMaxV;
76d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        // Minimize how much we call freetype
77d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        FT_UInt mGlyphIndex;
78d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        FT_Vector mAdvance;
79d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        // Values below contain a glyph's origin in the bitmap
80d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        FT_Int mBitmapLeft;
81d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        FT_Int mBitmapTop;
82d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        // Hold on to the bitmap in case cache is invalidated
83d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        FT_Bitmap mBitmap;
84d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        bool mBitmapValid;
85d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    };
86d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
87d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    String8 mFontName;
88d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    uint32_t mFontSize;
89d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    uint32_t mDpi;
90d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
91d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    Font(Context *rsc);
92d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    bool init(const char *name, uint32_t fontSize, uint32_t dpi);
93d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
94d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    FT_Face mFace;
95d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    bool mInitialized;
96d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    bool mHasKerning;
97d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
98d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    DefaultKeyedVector<uint32_t, CachedGlyphInfo* > mCachedGlyphs;
99d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
100d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    CachedGlyphInfo *cacheGlyph(uint32_t glyph);
101d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void updateGlyphCache(CachedGlyphInfo *glyph);
102d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y);
103d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk};
104d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
105d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchoukclass FontState
106d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk{
107d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchoukpublic:
108d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    FontState();
109d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    ~FontState();
110d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
111d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void init(Context *rsc);
112d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void deinit(Context *rsc);
113d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
114d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    ObjectBaseRef<Font> mDefault;
115d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    ObjectBaseRef<Font> mLast;
116d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
117d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y);
118d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void renderText(const char *text, int x, int y);
119d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void renderText(Allocation *alloc, int x, int y);
120d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void renderText(Allocation *alloc, uint32_t start, int len, int x, int y);
121d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
122d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchoukprotected:
123d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
124d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    friend class Font;
125d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
126d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    struct CacheTextureLine
127d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    {
128d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        uint32_t mMaxHeight;
129d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        uint32_t mMaxWidth;
130d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        uint32_t mCurrentRow;
131d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        uint32_t mCurrentCol;
132d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
133d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        CacheTextureLine(uint32_t maxHeight, uint32_t maxWidth, uint32_t currentRow, uint32_t currentCol) :
134d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk            mMaxHeight(maxHeight), mMaxWidth(maxWidth), mCurrentRow(currentRow), mCurrentCol(currentCol) {
135d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        }
136d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
137d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        bool fitBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
138d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk            if((uint32_t)bitmap->rows > mMaxHeight) {
139d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk                return false;
140d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk            }
141d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
142d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk            if(mCurrentCol + (uint32_t)bitmap->width < mMaxWidth) {
143d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk               *retOriginX = mCurrentCol;
144d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk               *retOriginY = mCurrentRow;
145d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk               mCurrentCol += bitmap->width;
146d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk               return true;
147d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk            }
148d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
149d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk            return false;
150d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        }
151d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    };
152d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
153d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    Vector<CacheTextureLine*> mCacheLines;
154d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
155d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    Context *mRSC;
156d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
157d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    // Free type library, we only need one copy
158d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    FT_Library mLibrary;
159d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    Vector<Font*> mActiveFonts;
160d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
161d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    // Render state for the font
162d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    ObjectBaseRef<ProgramFragment> mFontShaderF;
163d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    ObjectBaseRef<Sampler> mFontSampler;
164d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    ObjectBaseRef<ProgramStore> mFontProgramStore;
165d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void initRenderState();
166d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
167d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    // Texture to cache glyph bitmaps
168d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    ObjectBaseRef<Allocation> mTextTexture;
169d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void initTextTexture();
170d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
171d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    bool cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY);
172d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    const Type* getCacheTextureType() {
173d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk        return mTextTexture->getType();
174d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    }
175d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
176d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void flushAllAndInvalidate();
177d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
178d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    // Pointer to vertex data to speed up frame to frame work
179d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    float *mTextMeshPtr;
180d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    uint32_t mCurrentQuadIndex;
181d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    uint32_t mMaxNumberOfQuads;
182d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
183d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void initVertexArrayBuffers();
184d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    ObjectBaseRef<Allocation> mIndexBuffer;
185d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    ObjectBaseRef<Allocation> mVertexArray;
186d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
187d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
188d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    bool mInitialized;
189d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
190d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void checkInit();
191d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
192d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void issueDrawCommand();
193d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
194d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk    void appendMeshQuad(float x1, float y1, float z1,
195d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk                          float u1, float v1,
196d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk                          float x2, float y2, float z2,
197d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk                          float u2, float v2,
198d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk                          float x3, float y3, float z3,
199d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk                          float u3, float v3,
200d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk                          float x4, float y4, float z4,
201d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk                          float u4, float v4);
202d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
203d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk};
204d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
205d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
206d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk}
207d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk}
208d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk
209d3e0ad43dc758c409fc23d1893dab67b18520c24Alex Sakhartchouk#endif
210