blob: 4c626dd49303703062c9cc8bba1dc6d896d0c714 [file] [log] [blame]
Romain Guy01d58e42011-01-19 21:54:02 -08001/*
2 * Copyright (C) 2011 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
17#ifndef ANDROID_HWUI_SHAPE_CACHE_H
18#define ANDROID_HWUI_SHAPE_CACHE_H
19
20#include <GLES2/gl2.h>
21
Romain Guyff26a0c2011-01-20 11:35:46 -080022#include <SkBitmap.h>
Romain Guy01d58e42011-01-19 21:54:02 -080023#include <SkCanvas.h>
Romain Guyff26a0c2011-01-20 11:35:46 -080024#include <SkPaint.h>
25#include <SkPath.h>
Romain Guy01d58e42011-01-19 21:54:02 -080026#include <SkRect.h>
27
Romain Guyff26a0c2011-01-20 11:35:46 -080028#include "Debug.h"
Romain Guy01d58e42011-01-19 21:54:02 -080029#include "Properties.h"
Romain Guyff26a0c2011-01-20 11:35:46 -080030#include "Texture.h"
31#include "utils/Compare.h"
32#include "utils/GenerationCache.h"
Romain Guy01d58e42011-01-19 21:54:02 -080033
34namespace android {
35namespace uirenderer {
36
37///////////////////////////////////////////////////////////////////////////////
38// Defines
39///////////////////////////////////////////////////////////////////////////////
40
41// Debug
42#if DEBUG_SHAPES
43 #define SHAPE_LOGD(...) LOGD(__VA_ARGS__)
44#else
45 #define SHAPE_LOGD(...)
46#endif
47
48///////////////////////////////////////////////////////////////////////////////
49// Classes
50///////////////////////////////////////////////////////////////////////////////
51
52/**
Romain Guyff26a0c2011-01-20 11:35:46 -080053 * Alpha texture used to represent a path.
54 */
55struct PathTexture: public Texture {
56 PathTexture(): Texture() {
57 }
58
59 /**
60 * Left coordinate of the path bounds.
61 */
62 float left;
63 /**
64 * Top coordinate of the path bounds.
65 */
66 float top;
67 /**
68 * Offset to draw the path at the correct origin.
69 */
70 float offset;
71}; // struct PathTexture
72
73/**
Romain Guy01d58e42011-01-19 21:54:02 -080074 * Describe a shape in the shape cache.
75 */
76struct ShapeCacheEntry {
77 enum ShapeType {
78 kShapeNone,
Romain Guyc1cd9ba32011-01-23 14:18:41 -080079 kShapeRect,
Romain Guy01d58e42011-01-19 21:54:02 -080080 kShapeRoundRect,
81 kShapeCircle,
82 kShapeOval,
Romain Guyff26a0c2011-01-20 11:35:46 -080083 kShapeArc,
84 kShapePath
Romain Guy01d58e42011-01-19 21:54:02 -080085 };
86
87 ShapeCacheEntry() {
88 shapeType = kShapeNone;
89 join = SkPaint::kDefault_Join;
90 cap = SkPaint::kDefault_Cap;
91 style = SkPaint::kFill_Style;
92 miter = 4.0f;
93 strokeWidth = 1.0f;
94 }
95
96 ShapeCacheEntry(const ShapeCacheEntry& entry):
97 shapeType(entry.shapeType), join(entry.join), cap(entry.cap),
98 style(entry.style), miter(entry.miter),
99 strokeWidth(entry.strokeWidth) {
100 }
101
102 ShapeCacheEntry(ShapeType type, SkPaint* paint) {
103 shapeType = type;
104 join = paint->getStrokeJoin();
105 cap = paint->getStrokeCap();
106 float v = paint->getStrokeMiter();
107 miter = *(uint32_t*) &v;
108 v = paint->getStrokeWidth();
109 strokeWidth = *(uint32_t*) &v;
110 style = paint->getStyle();
111 }
112
113 virtual ~ShapeCacheEntry() {
114 }
115
116 // shapeType must be checked in subclasses operator<
117 ShapeType shapeType;
118 SkPaint::Join join;
119 SkPaint::Cap cap;
120 SkPaint::Style style;
121 uint32_t miter;
122 uint32_t strokeWidth;
123
124 bool operator<(const ShapeCacheEntry& rhs) const {
125 LTE_INT(shapeType) {
126 LTE_INT(join) {
127 LTE_INT(cap) {
128 LTE_INT(style) {
129 LTE_INT(miter) {
130 LTE_INT(strokeWidth) {
131 return lessThan(rhs);
132 }
133 }
134 }
135 }
136 }
137 }
138 return false;
139 }
140
141protected:
142 virtual bool lessThan(const ShapeCacheEntry& rhs) const {
143 return false;
144 }
145}; // struct ShapeCacheEntry
146
147
148struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
149 RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
150 ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
151 mWidth = *(uint32_t*) &width;
152 mHeight = *(uint32_t*) &height;
153 mRx = *(uint32_t*) &rx;
154 mRy = *(uint32_t*) &ry;
155 }
156
157 RoundRectShapeCacheEntry(): ShapeCacheEntry() {
158 mWidth = 0;
159 mHeight = 0;
160 mRx = 0;
161 mRy = 0;
162 }
163
164 RoundRectShapeCacheEntry(const RoundRectShapeCacheEntry& entry):
165 ShapeCacheEntry(entry) {
166 mWidth = entry.mWidth;
167 mHeight = entry.mHeight;
168 mRx = entry.mRx;
169 mRy = entry.mRy;
170 }
171
172 bool lessThan(const ShapeCacheEntry& r) const {
173 const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
174 LTE_INT(mWidth) {
175 LTE_INT(mHeight) {
176 LTE_INT(mRx) {
177 LTE_INT(mRy) {
178 return false;
179 }
180 }
181 }
182 }
183 return false;
184 }
185
186private:
187 uint32_t mWidth;
188 uint32_t mHeight;
189 uint32_t mRx;
190 uint32_t mRy;
191}; // RoundRectShapeCacheEntry
192
193struct CircleShapeCacheEntry: public ShapeCacheEntry {
194 CircleShapeCacheEntry(float radius, SkPaint* paint):
195 ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
196 mRadius = *(uint32_t*) &radius;
197 }
198
199 CircleShapeCacheEntry(): ShapeCacheEntry() {
200 mRadius = 0;
201 }
202
203 CircleShapeCacheEntry(const CircleShapeCacheEntry& entry):
204 ShapeCacheEntry(entry) {
205 mRadius = entry.mRadius;
206 }
207
208 bool lessThan(const ShapeCacheEntry& r) const {
209 const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
210 LTE_INT(mRadius) {
211 return false;
212 }
213 return false;
214 }
215
216private:
217 uint32_t mRadius;
218}; // CircleShapeCacheEntry
219
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800220struct OvalShapeCacheEntry: public ShapeCacheEntry {
221 OvalShapeCacheEntry(float width, float height, SkPaint* paint):
222 ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
223 mWidth = *(uint32_t*) &width;
224 mHeight = *(uint32_t*) &height;
225 }
226
227 OvalShapeCacheEntry(): ShapeCacheEntry() {
228 mWidth = mHeight = 0;
229 }
230
231 OvalShapeCacheEntry(const OvalShapeCacheEntry& entry):
232 ShapeCacheEntry(entry) {
233 mWidth = entry.mWidth;
234 mHeight = entry.mHeight;
235 }
236
237 bool lessThan(const ShapeCacheEntry& r) const {
238 const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r;
239 LTE_INT(mWidth) {
240 LTE_INT(mHeight) {
241 return false;
242 }
243 }
244 return false;
245 }
246
247private:
248 uint32_t mWidth;
249 uint32_t mHeight;
250}; // OvalShapeCacheEntry
251
252struct RectShapeCacheEntry: public ShapeCacheEntry {
253 RectShapeCacheEntry(float width, float height, SkPaint* paint):
254 ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
255 mWidth = *(uint32_t*) &width;
256 mHeight = *(uint32_t*) &height;
257 }
258
259 RectShapeCacheEntry(): ShapeCacheEntry() {
260 mWidth = mHeight = 0;
261 }
262
263 RectShapeCacheEntry(const RectShapeCacheEntry& entry):
264 ShapeCacheEntry(entry) {
265 mWidth = entry.mWidth;
266 mHeight = entry.mHeight;
267 }
268
269 bool lessThan(const ShapeCacheEntry& r) const {
270 const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r;
271 LTE_INT(mWidth) {
272 LTE_INT(mHeight) {
273 return false;
274 }
275 }
276 return false;
277 }
278
279private:
280 uint32_t mWidth;
281 uint32_t mHeight;
282}; // RectShapeCacheEntry
283
Romain Guy8b2f5262011-01-23 16:15:02 -0800284struct ArcShapeCacheEntry: public ShapeCacheEntry {
285 ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle,
286 bool useCenter, SkPaint* paint):
287 ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) {
288 mWidth = *(uint32_t*) &width;
289 mHeight = *(uint32_t*) &height;
290 mStartAngle = *(uint32_t*) &startAngle;
291 mSweepAngle = *(uint32_t*) &sweepAngle;
292 mUseCenter = useCenter ? 1 : 0;
293 }
294
295 ArcShapeCacheEntry(): ShapeCacheEntry() {
296 mWidth = 0;
297 mHeight = 0;
298 mStartAngle = 0;
299 mSweepAngle = 0;
300 mUseCenter = 0;
301 }
302
303 ArcShapeCacheEntry(const ArcShapeCacheEntry& entry):
304 ShapeCacheEntry(entry) {
305 mWidth = entry.mWidth;
306 mHeight = entry.mHeight;
307 mStartAngle = entry.mStartAngle;
308 mSweepAngle = entry.mSweepAngle;
309 mUseCenter = entry.mUseCenter;
310 }
311
312 bool lessThan(const ShapeCacheEntry& r) const {
313 const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r;
314 LTE_INT(mWidth) {
315 LTE_INT(mHeight) {
316 LTE_INT(mStartAngle) {
317 LTE_INT(mSweepAngle) {
318 LTE_INT(mUseCenter) {
319 return false;
320 }
321 }
322 }
323 }
324 }
325 return false;
326 }
327
328private:
329 uint32_t mWidth;
330 uint32_t mHeight;
331 uint32_t mStartAngle;
332 uint32_t mSweepAngle;
333 uint32_t mUseCenter;
334}; // ArcShapeCacheEntry
335
Romain Guy01d58e42011-01-19 21:54:02 -0800336/**
337 * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
338 * Any texture added to the cache causing the cache to grow beyond the maximum
339 * allowed size will also cause the oldest texture to be kicked out.
340 */
341template<typename Entry>
342class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
343public:
Romain Guyff26a0c2011-01-20 11:35:46 -0800344 ShapeCache(const char* name, const char* propertyName, float defaultSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800345 ~ShapeCache();
346
347 /**
348 * Used as a callback when an entry is removed from the cache.
349 * Do not invoke directly.
350 */
351 void operator()(Entry& path, PathTexture*& texture);
352
353 /**
354 * Clears the cache. This causes all textures to be deleted.
355 */
356 void clear();
357
358 /**
359 * Sets the maximum size of the cache in bytes.
360 */
361 void setMaxSize(uint32_t maxSize);
362 /**
363 * Returns the maximum size of the cache in bytes.
364 */
365 uint32_t getMaxSize();
366 /**
367 * Returns the current size of the cache in bytes.
368 */
369 uint32_t getSize();
370
371protected:
372 PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
373
374 PathTexture* get(Entry entry) {
375 return mCache.get(entry);
376 }
377
Romain Guy01d58e42011-01-19 21:54:02 -0800378 void removeTexture(PathTexture* texture);
379
Romain Guy01d58e42011-01-19 21:54:02 -0800380 GenerationCache<Entry, PathTexture*> mCache;
381 uint32_t mSize;
382 uint32_t mMaxSize;
383 GLuint mMaxTextureSize;
384
Romain Guyff26a0c2011-01-20 11:35:46 -0800385 char* mName;
Romain Guy01d58e42011-01-19 21:54:02 -0800386 bool mDebugEnabled;
Romain Guyff26a0c2011-01-20 11:35:46 -0800387
388private:
389 /**
390 * Generates the texture from a bitmap into the specified texture structure.
391 */
392 void generateTexture(SkBitmap& bitmap, Texture* texture);
393
394 void init();
Romain Guy01d58e42011-01-19 21:54:02 -0800395}; // class ShapeCache
396
397class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
398public:
399 RoundRectShapeCache();
400
401 PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
402}; // class RoundRectShapeCache
403
404class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
405public:
406 CircleShapeCache();
407
408 PathTexture* getCircle(float radius, SkPaint* paint);
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800409}; // class CircleShapeCache
Romain Guy01d58e42011-01-19 21:54:02 -0800410
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800411class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
412public:
413 OvalShapeCache();
414
415 PathTexture* getOval(float width, float height, SkPaint* paint);
416}; // class OvalShapeCache
417
418class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
419public:
420 RectShapeCache();
421
422 PathTexture* getRect(float width, float height, SkPaint* paint);
423}; // class RectShapeCache
Romain Guy01d58e42011-01-19 21:54:02 -0800424
Romain Guy8b2f5262011-01-23 16:15:02 -0800425class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
426public:
427 ArcShapeCache();
428
429 PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
430 bool useCenter, SkPaint* paint);
431}; // class ArcShapeCache
432
Romain Guy01d58e42011-01-19 21:54:02 -0800433///////////////////////////////////////////////////////////////////////////////
434// Constructors/destructor
435///////////////////////////////////////////////////////////////////////////////
436
437template<class Entry>
Romain Guyff26a0c2011-01-20 11:35:46 -0800438ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
Romain Guy01d58e42011-01-19 21:54:02 -0800439 mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
Romain Guyff26a0c2011-01-20 11:35:46 -0800440 mSize(0), mMaxSize(MB(defaultSize)) {
Romain Guy01d58e42011-01-19 21:54:02 -0800441 char property[PROPERTY_VALUE_MAX];
Romain Guyff26a0c2011-01-20 11:35:46 -0800442 if (property_get(propertyName, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800443 INIT_LOGD(" Setting %s cache size to %sMB", name, property);
Romain Guy01d58e42011-01-19 21:54:02 -0800444 setMaxSize(MB(atof(property)));
445 } else {
Romain Guyc9855a52011-01-21 21:14:15 -0800446 INIT_LOGD(" Using default %s cache size of %.2fMB", name, defaultSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800447 }
Romain Guy01d58e42011-01-19 21:54:02 -0800448
Romain Guyff26a0c2011-01-20 11:35:46 -0800449 size_t len = strlen(name);
450 mName = new char[len + 1];
451 strcpy(mName, name);
452 mName[len] = '\0';
453
Romain Guy01d58e42011-01-19 21:54:02 -0800454 init();
455}
456
457template<class Entry>
458ShapeCache<Entry>::~ShapeCache() {
459 mCache.clear();
Romain Guyff26a0c2011-01-20 11:35:46 -0800460 delete[] mName;
Romain Guy01d58e42011-01-19 21:54:02 -0800461}
462
463template<class Entry>
464void ShapeCache<Entry>::init() {
465 mCache.setOnEntryRemovedListener(this);
466
467 GLint maxTextureSize;
468 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
469 mMaxTextureSize = maxTextureSize;
470
471 mDebugEnabled = readDebugLevel() & kDebugCaches;
472}
473
474///////////////////////////////////////////////////////////////////////////////
475// Size management
476///////////////////////////////////////////////////////////////////////////////
477
478template<class Entry>
479uint32_t ShapeCache<Entry>::getSize() {
480 return mSize;
481}
482
483template<class Entry>
484uint32_t ShapeCache<Entry>::getMaxSize() {
485 return mMaxSize;
486}
487
488template<class Entry>
489void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
490 mMaxSize = maxSize;
491 while (mSize > mMaxSize) {
492 mCache.removeOldest();
493 }
494}
495
496///////////////////////////////////////////////////////////////////////////////
497// Callbacks
498///////////////////////////////////////////////////////////////////////////////
499
500template<class Entry>
501void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
502 removeTexture(texture);
503}
504
505///////////////////////////////////////////////////////////////////////////////
506// Caching
507///////////////////////////////////////////////////////////////////////////////
508
509template<class Entry>
510void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
511 if (texture) {
512 const uint32_t size = texture->width * texture->height;
513 mSize -= size;
514
Romain Guyff26a0c2011-01-20 11:35:46 -0800515 SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
516 mName, texture->id, size, mSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800517 if (mDebugEnabled) {
Romain Guyff26a0c2011-01-20 11:35:46 -0800518 LOGD("Shape %s deleted, size = %d", mName, size);
Romain Guy01d58e42011-01-19 21:54:02 -0800519 }
520
521 glDeleteTextures(1, &texture->id);
522 delete texture;
523 }
524}
525
526template<class Entry>
527PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
528 const SkPaint* paint) {
529 const SkRect& bounds = path->getBounds();
530
531 const float pathWidth = fmax(bounds.width(), 1.0f);
532 const float pathHeight = fmax(bounds.height(), 1.0f);
533
534 if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) {
Romain Guyff26a0c2011-01-20 11:35:46 -0800535 LOGW("Shape %s too large to be rendered into a texture", mName);
Romain Guy01d58e42011-01-19 21:54:02 -0800536 return NULL;
537 }
538
539 const float offset = paint->getStrokeWidth() * 1.5f;
540 const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
541 const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
542
543 const uint32_t size = width * height;
544 // Don't even try to cache a bitmap that's bigger than the cache
545 if (size < mMaxSize) {
546 while (mSize + size > mMaxSize) {
547 mCache.removeOldest();
548 }
549 }
550
551 PathTexture* texture = new PathTexture;
552 texture->left = bounds.fLeft;
553 texture->top = bounds.fTop;
554 texture->offset = offset;
555 texture->width = width;
556 texture->height = height;
557 texture->generation = path->getGenerationID();
558
559 SkBitmap bitmap;
560 bitmap.setConfig(SkBitmap::kA8_Config, width, height);
561 bitmap.allocPixels();
562 bitmap.eraseColor(0);
563
564 SkPaint pathPaint(*paint);
565
566 // Make sure the paint is opaque, color, alpha, filter, etc.
567 // will be applied later when compositing the alpha8 texture
568 pathPaint.setColor(0xff000000);
569 pathPaint.setAlpha(255);
570 pathPaint.setColorFilter(NULL);
571 pathPaint.setMaskFilter(NULL);
572 pathPaint.setShader(NULL);
573 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
Derek Sollenberger6062c592011-02-22 13:55:04 -0500574 SkSafeUnref(pathPaint.setXfermode(mode));
Romain Guy01d58e42011-01-19 21:54:02 -0800575
576 SkCanvas canvas(bitmap);
577 canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset);
578 canvas.drawPath(*path, pathPaint);
579
580 generateTexture(bitmap, texture);
581
582 if (size < mMaxSize) {
583 mSize += size;
Romain Guyff26a0c2011-01-20 11:35:46 -0800584 SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
585 mName, texture->id, size, mSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800586 if (mDebugEnabled) {
Romain Guyff26a0c2011-01-20 11:35:46 -0800587 LOGD("Shape %s created, size = %d", mName, size);
Romain Guy01d58e42011-01-19 21:54:02 -0800588 }
589 mCache.put(entry, texture);
590 } else {
591 texture->cleanup = true;
592 }
593
594 return texture;
595}
596
597template<class Entry>
598void ShapeCache<Entry>::clear() {
599 mCache.clear();
600}
601
602template<class Entry>
603void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
604 SkAutoLockPixels alp(bitmap);
605 if (!bitmap.readyToDraw()) {
606 LOGE("Cannot generate texture from bitmap");
607 return;
608 }
609
610 glGenTextures(1, &texture->id);
611
612 glBindTexture(GL_TEXTURE_2D, texture->id);
613 // Textures are Alpha8
614 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
615
616 texture->blend = true;
617 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
618 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
619
620 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
621 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
622
623 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
624 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
625}
626
627}; // namespace uirenderer
628}; // namespace android
629
630#endif // ANDROID_HWUI_SHAPE_CACHE_H