blob: 021cad9476052303128da630e8ef32967fbfd614 [file] [log] [blame]
Romain Guy694b5192010-07-21 21:33:20 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Romain Guy5b3b3522010-10-27 18:57:51 -070017#ifndef ANDROID_HWUI_FONT_RENDERER_H
18#define ANDROID_HWUI_FONT_RENDERER_H
Romain Guy694b5192010-07-21 21:33:20 -070019
20#include <utils/String8.h>
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -070021#include <utils/String16.h>
Romain Guy694b5192010-07-21 21:33:20 -070022#include <utils/Vector.h>
23#include <utils/KeyedVector.h>
24
25#include <SkScalerContext.h>
26#include <SkPaint.h>
Romain Guy97771732012-02-28 18:17:02 -080027#include <SkPathMeasure.h>
28#include <SkPoint.h>
Romain Guy694b5192010-07-21 21:33:20 -070029
30#include <GLES2/gl2.h>
31
Romain Guy09147fb2010-07-22 13:08:20 -070032#include "Rect.h"
Romain Guy51769a62010-07-23 00:28:00 -070033#include "Properties.h"
Romain Guy09147fb2010-07-22 13:08:20 -070034
Romain Guy694b5192010-07-21 21:33:20 -070035namespace android {
36namespace uirenderer {
37
Romain Guy726aeba2011-06-01 14:52:00 -070038///////////////////////////////////////////////////////////////////////////////
39// Defines
40///////////////////////////////////////////////////////////////////////////////
41
42#if RENDER_TEXT_AS_GLYPHS
43 typedef uint16_t glyph_t;
Romain Guyae91c4c2012-05-14 14:00:27 -070044 #define TO_GLYPH(g) g
Romain Guy726aeba2011-06-01 14:52:00 -070045 #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
46 #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
47 #define IS_END_OF_STRING(glyph) false
48#else
49 typedef SkUnichar glyph_t;
Romain Guyae91c4c2012-05-14 14:00:27 -070050 #define TO_GLYPH(g) ((SkUnichar) g)
Romain Guy726aeba2011-06-01 14:52:00 -070051 #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
52 #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
53 #define IS_END_OF_STRING(glyph) glyph < 0
54#endif
55
56///////////////////////////////////////////////////////////////////////////////
57// Declarations
58///////////////////////////////////////////////////////////////////////////////
59
Romain Guy694b5192010-07-21 21:33:20 -070060class FontRenderer;
61
Chet Haase7de0cb12011-12-05 16:35:38 -080062class CacheTexture {
63public:
Romain Guy9d9758a2012-05-14 15:19:58 -070064 CacheTexture() { }
Romain Guy99a6ddd2012-05-14 15:32:18 -070065 CacheTexture(uint8_t* texture, uint16_t width, uint16_t height) :
66 mTexture(texture), mTextureId(0), mWidth(width), mHeight(height),
Romain Guy9d9758a2012-05-14 15:19:58 -070067 mLinearFiltering(false) { }
Chet Haase7de0cb12011-12-05 16:35:38 -080068 ~CacheTexture() {
Romain Guy9d9758a2012-05-14 15:19:58 -070069 if (mTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -080070 delete[] mTexture;
71 }
Romain Guy9d9758a2012-05-14 15:19:58 -070072 if (mTextureId) {
Chet Haase7de0cb12011-12-05 16:35:38 -080073 glDeleteTextures(1, &mTextureId);
74 }
75 }
76
77 uint8_t* mTexture;
78 GLuint mTextureId;
79 uint16_t mWidth;
80 uint16_t mHeight;
Chet Haase2a47c142011-12-14 15:22:56 -080081 bool mLinearFiltering;
Chet Haase7de0cb12011-12-05 16:35:38 -080082};
83
84class CacheTextureLine {
85public:
86 CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow,
87 uint32_t currentCol, CacheTexture* cacheTexture):
88 mMaxHeight(maxHeight),
89 mMaxWidth(maxWidth),
90 mCurrentRow(currentRow),
91 mCurrentCol(currentCol),
92 mDirty(false),
Romain Guy9d9758a2012-05-14 15:19:58 -070093 mCacheTexture(cacheTexture) {
Chet Haase7de0cb12011-12-05 16:35:38 -080094 }
95
96 bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
97
98 uint16_t mMaxHeight;
99 uint16_t mMaxWidth;
100 uint32_t mCurrentRow;
101 uint32_t mCurrentCol;
102 bool mDirty;
Romain Guyae91c4c2012-05-14 14:00:27 -0700103 CacheTexture* mCacheTexture;
Chet Haase7de0cb12011-12-05 16:35:38 -0800104};
105
106struct CachedGlyphInfo {
107 // Has the cache been invalidated?
108 bool mIsValid;
109 // Location of the cached glyph in the bitmap
110 // in case we need to resize the texture or
111 // render to bitmap
112 uint32_t mStartX;
113 uint32_t mStartY;
114 uint32_t mBitmapWidth;
115 uint32_t mBitmapHeight;
116 // Also cache texture coords for the quad
117 float mBitmapMinU;
118 float mBitmapMinV;
119 float mBitmapMaxU;
120 float mBitmapMaxV;
121 // Minimize how much we call freetype
122 uint32_t mGlyphIndex;
123 uint32_t mAdvanceX;
124 uint32_t mAdvanceY;
125 // Values below contain a glyph's origin in the bitmap
126 int32_t mBitmapLeft;
127 int32_t mBitmapTop;
128 // Auto-kerning
129 SkFixed mLsbDelta;
130 SkFixed mRsbDelta;
131 CacheTextureLine* mCachedTextureLine;
132};
133
134
Romain Guy726aeba2011-06-01 14:52:00 -0700135///////////////////////////////////////////////////////////////////////////////
136// Font
137///////////////////////////////////////////////////////////////////////////////
138
Romain Guy51769a62010-07-23 00:28:00 -0700139/**
140 * Represents a font, defined by a Skia font id and a font size. A font is used
141 * to generate glyphs and cache them in the FontState.
142 */
Romain Guy694b5192010-07-21 21:33:20 -0700143class Font {
144public:
Romain Guy325a0f92011-01-05 15:26:55 -0800145 enum Style {
Romain Guyc7b25be2011-03-23 14:59:20 -0700146 kFakeBold = 1
Romain Guy325a0f92011-01-05 15:26:55 -0800147 };
148
Romain Guy694b5192010-07-21 21:33:20 -0700149 ~Font();
150
Romain Guy51769a62010-07-23 00:28:00 -0700151 /**
152 * Renders the specified string of text.
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700153 * If bitmap is specified, it will be used as the render target
Romain Guy51769a62010-07-23 00:28:00 -0700154 */
Romain Guy726aeba2011-06-01 14:52:00 -0700155 void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
156 int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
157 uint32_t bitmapW = 0, uint32_t bitmapH = 0);
Romain Guy671d6cf2012-01-18 12:39:17 -0800158
159 void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
160 int numGlyphs, int x, int y, const float* positions);
161
Romain Guy97771732012-02-28 18:17:02 -0800162 void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
163 int numGlyphs, SkPath* path, float hOffset, float vOffset);
164
Romain Guy51769a62010-07-23 00:28:00 -0700165 /**
166 * Creates a new font associated with the specified font state.
167 */
Romain Guy2577db12011-01-18 13:02:38 -0800168 static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
Romain Guybd496bc2011-08-02 17:32:41 -0700169 int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
170 uint32_t strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700171
172protected:
Romain Guy694b5192010-07-21 21:33:20 -0700173 friend class FontRenderer;
Romain Guy671d6cf2012-01-18 12:39:17 -0800174 typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
175 uint32_t, uint32_t, Rect*, const float*);
Romain Guy694b5192010-07-21 21:33:20 -0700176
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700177 enum RenderMode {
178 FRAMEBUFFER,
179 BITMAP,
180 MEASURE,
181 };
182
Romain Guy726aeba2011-06-01 14:52:00 -0700183 void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
184 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
Romain Guy671d6cf2012-01-18 12:39:17 -0800185 uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700186
Romain Guy726aeba2011-06-01 14:52:00 -0700187 void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
Raph Levien416a8472012-07-19 22:48:17 -0700188 int numGlyphs, Rect *bounds, const float* positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700189
Chet Haase8668f8a2011-03-02 13:51:36 -0800190 Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
Romain Guybd496bc2011-08-02 17:32:41 -0700191 uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
Romain Guy694b5192010-07-21 21:33:20 -0700192
Romain Guy726aeba2011-06-01 14:52:00 -0700193 // Cache of glyphs
194 DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
Romain Guy694b5192010-07-21 21:33:20 -0700195
Chet Haase9a824562011-12-16 15:44:59 -0800196 void invalidateTextureCache(CacheTextureLine *cacheLine = NULL);
Romain Guybd0e6aa2010-07-22 18:50:12 -0700197
Romain Guy726aeba2011-06-01 14:52:00 -0700198 CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph);
Romain Guy671d6cf2012-01-18 12:39:17 -0800199 void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph);
200
201 void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
202 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
203 Rect* bounds, const float* pos);
204 void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
205 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
206 Rect* bounds, const float* pos);
207 void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
208 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
209 Rect* bounds, const float* pos);
Romain Guy97771732012-02-28 18:17:02 -0800210 void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
211 SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
Romain Guybd0e6aa2010-07-22 18:50:12 -0700212
Romain Guy726aeba2011-06-01 14:52:00 -0700213 CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit);
214
215 static glyph_t nextGlyph(const uint16_t** srcPtr) {
216 const uint16_t* src = *srcPtr;
217 glyph_t g = *src++;
218 *srcPtr = src;
219 return g;
220 }
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700221
Romain Guybd0e6aa2010-07-22 18:50:12 -0700222 FontRenderer* mState;
223 uint32_t mFontId;
224 float mFontSize;
Romain Guy325a0f92011-01-05 15:26:55 -0800225 int mFlags;
Romain Guy2577db12011-01-18 13:02:38 -0800226 uint32_t mItalicStyle;
Chet Haase8668f8a2011-03-02 13:51:36 -0800227 uint32_t mScaleX;
Romain Guybd496bc2011-08-02 17:32:41 -0700228 SkPaint::Style mStyle;
229 uint32_t mStrokeWidth;
Romain Guy694b5192010-07-21 21:33:20 -0700230};
231
Romain Guy726aeba2011-06-01 14:52:00 -0700232///////////////////////////////////////////////////////////////////////////////
233// Renderer
234///////////////////////////////////////////////////////////////////////////////
235
Romain Guy694b5192010-07-21 21:33:20 -0700236class FontRenderer {
237public:
238 FontRenderer();
239 ~FontRenderer();
240
Chet Haase9a824562011-12-16 15:44:59 -0800241 void flushLargeCaches();
Romain Guy694b5192010-07-21 21:33:20 -0700242
Romain Guyb45c0c92010-08-26 20:35:23 -0700243 void setGammaTable(const uint8_t* gammaTable) {
244 mGammaTable = gammaTable;
245 }
246
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700247 void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
Romain Guy671d6cf2012-01-18 12:39:17 -0800248 // bounds is an out parameter
Romain Guy5b3b3522010-10-27 18:57:51 -0700249 bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
250 uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
Romain Guy671d6cf2012-01-18 12:39:17 -0800251 // bounds is an out parameter
252 bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
253 uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds);
Romain Guy97771732012-02-28 18:17:02 -0800254 // bounds is an out parameter
255 bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
256 uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds);
Romain Guy694b5192010-07-21 21:33:20 -0700257
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700258 struct DropShadow {
Romain Guy1e45aae2010-08-13 19:39:53 -0700259 DropShadow() { };
260
261 DropShadow(const DropShadow& dropShadow):
262 width(dropShadow.width), height(dropShadow.height),
263 image(dropShadow.image), penX(dropShadow.penX),
264 penY(dropShadow.penY) {
265 }
266
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700267 uint32_t width;
268 uint32_t height;
269 uint8_t* image;
270 int32_t penX;
271 int32_t penY;
272 };
273
274 // After renderDropShadow returns, the called owns the memory in DropShadow.image
275 // and is responsible for releasing it when it's done with it
276 DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex,
Raph Levien416a8472012-07-19 22:48:17 -0700277 uint32_t len, int numGlyphs, uint32_t radius, const float* positions);
Alex Sakhartchoukf18136c2010-08-06 14:49:04 -0700278
Romain Guye8cb9c142010-10-04 14:14:11 -0700279 GLuint getTexture(bool linearFiltering = false) {
Romain Guy694b5192010-07-21 21:33:20 -0700280 checkInit();
Romain Guyae91c4c2012-05-14 14:00:27 -0700281
Chet Haase2a47c142011-12-14 15:22:56 -0800282 if (linearFiltering != mCurrentCacheTexture->mLinearFiltering) {
283 mCurrentCacheTexture->mLinearFiltering = linearFiltering;
Romain Guye8cb9c142010-10-04 14:14:11 -0700284 mLinearFiltering = linearFiltering;
285 const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
286
Chet Haase7de0cb12011-12-05 16:35:38 -0800287 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
Romain Guye8cb9c142010-10-04 14:14:11 -0700288 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
289 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
290 }
Romain Guyae91c4c2012-05-14 14:00:27 -0700291
Chet Haase7de0cb12011-12-05 16:35:38 -0800292 return mCurrentCacheTexture->mTextureId;
Romain Guy694b5192010-07-21 21:33:20 -0700293 }
294
Chet Haase7de0cb12011-12-05 16:35:38 -0800295 uint32_t getCacheSize() const {
296 uint32_t size = 0;
297 if (mCacheTextureSmall != NULL && mCacheTextureSmall->mTexture != NULL) {
298 size += mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight;
299 }
300 if (mCacheTexture128 != NULL && mCacheTexture128->mTexture != NULL) {
301 size += mCacheTexture128->mWidth * mCacheTexture128->mHeight;
302 }
303 if (mCacheTexture256 != NULL && mCacheTexture256->mTexture != NULL) {
304 size += mCacheTexture256->mWidth * mCacheTexture256->mHeight;
305 }
306 if (mCacheTexture512 != NULL && mCacheTexture512->mTexture != NULL) {
307 size += mCacheTexture512->mWidth * mCacheTexture512->mHeight;
308 }
309 return size;
Romain Guyc15008e2010-11-10 11:59:15 -0800310 }
311
Romain Guy694b5192010-07-21 21:33:20 -0700312protected:
313 friend class Font;
314
Romain Guyb45c0c92010-08-26 20:35:23 -0700315 const uint8_t* mGammaTable;
316
Chet Haase2a47c142011-12-14 15:22:56 -0800317 void allocateTextureMemory(CacheTexture* cacheTexture);
Chet Haase9a824562011-12-16 15:44:59 -0800318 void deallocateTextureMemory(CacheTexture* cacheTexture);
Chet Haase7de0cb12011-12-05 16:35:38 -0800319 void initTextTexture();
Romain Guy97771732012-02-28 18:17:02 -0800320 CacheTexture* createCacheTexture(int width, int height, bool allocate);
Chet Haase7de0cb12011-12-05 16:35:38 -0800321 void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
322 uint32_t *retOriginX, uint32_t *retOriginY);
Romain Guy694b5192010-07-21 21:33:20 -0700323
324 void flushAllAndInvalidate();
325 void initVertexArrayBuffers();
326
327 void checkInit();
Romain Guy671d6cf2012-01-18 12:39:17 -0800328 void initRender(const Rect* clip, Rect* bounds);
329 void finishRender();
Romain Guy694b5192010-07-21 21:33:20 -0700330
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700331 void precacheLatin(SkPaint* paint);
332
Romain Guy694b5192010-07-21 21:33:20 -0700333 void issueDrawCommand();
Romain Guy97771732012-02-28 18:17:02 -0800334 void appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
335 float x2, float y2, float u2, float v2,
336 float x3, float y3, float u3, float v3,
337 float x4, float y4, float u4, float v4, CacheTexture* texture);
Romain Guyd71dd362011-12-12 19:03:35 -0800338 void appendMeshQuad(float x1, float y1, float u1, float v1,
339 float x2, float y2, float u2, float v2,
340 float x3, float y3, float u3, float v3,
Chet Haase7de0cb12011-12-05 16:35:38 -0800341 float x4, float y4, float u4, float v4, CacheTexture* texture);
Romain Guy97771732012-02-28 18:17:02 -0800342 void appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
343 float x2, float y2, float u2, float v2,
344 float x3, float y3, float u3, float v3,
345 float x4, float y4, float u4, float v4, CacheTexture* texture);
Romain Guy694b5192010-07-21 21:33:20 -0700346
Chet Haase7de0cb12011-12-05 16:35:38 -0800347 uint32_t mSmallCacheWidth;
348 uint32_t mSmallCacheHeight;
Romain Guy694b5192010-07-21 21:33:20 -0700349
Romain Guy694b5192010-07-21 21:33:20 -0700350 Vector<CacheTextureLine*> mCacheLines;
Alex Sakhartchouk65ef9092010-07-26 11:09:33 -0700351 uint32_t getRemainingCacheCapacity();
Romain Guy694b5192010-07-21 21:33:20 -0700352
Romain Guy09147fb2010-07-22 13:08:20 -0700353 Font* mCurrentFont;
Romain Guy694b5192010-07-21 21:33:20 -0700354 Vector<Font*> mActiveFonts;
355
Chet Haase7de0cb12011-12-05 16:35:38 -0800356 CacheTexture* mCurrentCacheTexture;
357 CacheTexture* mLastCacheTexture;
358 CacheTexture* mCacheTextureSmall;
359 CacheTexture* mCacheTexture128;
360 CacheTexture* mCacheTexture256;
361 CacheTexture* mCacheTexture512;
362
Alex Sakhartchouk9b9902d2010-07-23 14:45:49 -0700363 void checkTextureUpdate();
Romain Guy694b5192010-07-21 21:33:20 -0700364 bool mUploadTexture;
365
366 // Pointer to vertex data to speed up frame to frame work
367 float *mTextMeshPtr;
368 uint32_t mCurrentQuadIndex;
369 uint32_t mMaxNumberOfQuads;
370
371 uint32_t mIndexBufferID;
372
Romain Guy09147fb2010-07-22 13:08:20 -0700373 const Rect* mClip;
Romain Guy5b3b3522010-10-27 18:57:51 -0700374 Rect* mBounds;
375 bool mDrawn;
Romain Guy09147fb2010-07-22 13:08:20 -0700376
Romain Guy694b5192010-07-21 21:33:20 -0700377 bool mInitialized;
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700378
Romain Guye8cb9c142010-10-04 14:14:11 -0700379 bool mLinearFiltering;
380
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700381 void computeGaussianWeights(float* weights, int32_t radius);
382 void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
Romain Guy1e45aae2010-08-13 19:39:53 -0700383 int32_t width, int32_t height);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700384 void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
Romain Guy1e45aae2010-08-13 19:39:53 -0700385 int32_t width, int32_t height);
Alex Sakhartchouk89a524a2010-08-02 17:52:30 -0700386 void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
Romain Guy694b5192010-07-21 21:33:20 -0700387};
388
389}; // namespace uirenderer
390}; // namespace android
391
Romain Guy5b3b3522010-10-27 18:57:51 -0700392#endif // ANDROID_HWUI_FONT_RENDERER_H