35d3fd51056c9382ec887db6e9b9d8358fc88ed7
[platform/upstream/harfbuzz.git] / src / hb-ot-color-sbix-table.hh
1 /*
2  * Copyright © 2018  Ebrahim Byagowi
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  */
24
25 #ifndef HB_OT_COLOR_SBIX_TABLE_HH
26 #define HB_OT_COLOR_SBIX_TABLE_HH
27
28 #include "hb-open-type.hh"
29
30 /*
31  * sbix -- Standard Bitmap Graphics
32  * https://docs.microsoft.com/en-us/typography/opentype/spec/sbix
33  * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html
34  */
35 #define HB_OT_TAG_sbix HB_TAG('s','b','i','x')
36
37
38 namespace OT {
39
40
41 struct SBIXGlyph
42 {
43   HBINT16       xOffset;        /* The horizontal (x-axis) offset from the left
44                                  * edge of the graphic to the glyph’s origin.
45                                  * That is, the x-coordinate of the point on the
46                                  * baseline at the left edge of the glyph. */
47   HBINT16       yOffset;        /* The vertical (y-axis) offset from the bottom
48                                  * edge of the graphic to the glyph’s origin.
49                                  * That is, the y-coordinate of the point on the
50                                  * baseline at the left edge of the glyph. */
51   Tag           graphicType;    /* Indicates the format of the embedded graphic
52                                  * data: one of 'jpg ', 'png ' or 'tiff', or the
53                                  * special format 'dupe'. */
54   UnsizedArrayOf<HBUINT8>
55                 data;           /* The actual embedded graphic data. The total
56                                  * length is inferred from sequential entries in
57                                  * the glyphDataOffsets array and the fixed size
58                                  * (8 bytes) of the preceding fields. */
59   public:
60   DEFINE_SIZE_ARRAY (8, data);
61 };
62
63 struct SBIXStrike
64 {
65   bool sanitize (hb_sanitize_context_t *c) const
66   {
67     TRACE_SANITIZE (this);
68     return_trace (c->check_struct (this) &&
69                   imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1));
70   }
71
72   hb_blob_t *get_glyph_blob (unsigned int  glyph_id,
73                              hb_blob_t    *sbix_blob,
74                              hb_tag_t      file_type,
75                              int          *x_offset,
76                              int          *y_offset,
77                              unsigned int  num_glyphs,
78                              unsigned int *strike_ppem) const
79   {
80     if (unlikely (!ppem)) return hb_blob_get_empty (); /* To get Null() object out of the way. */
81
82     unsigned int retry_count = 8;
83     unsigned int sbix_len = sbix_blob->length;
84     unsigned int strike_offset = (const char *) this - (const char *) sbix_blob->data;
85     assert (strike_offset < sbix_len);
86
87   retry:
88     if (unlikely (glyph_id >= num_glyphs ||
89                   imageOffsetsZ[glyph_id + 1] <= imageOffsetsZ[glyph_id] ||
90                   imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] <= SBIXGlyph::min_size ||
91                   (unsigned int) imageOffsetsZ[glyph_id + 1] > sbix_len - strike_offset))
92       return hb_blob_get_empty ();
93
94     unsigned int glyph_offset = strike_offset + (unsigned int) imageOffsetsZ[glyph_id] + SBIXGlyph::min_size;
95     unsigned int glyph_length = imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] - SBIXGlyph::min_size;
96
97     const SBIXGlyph *glyph = &(this+imageOffsetsZ[glyph_id]);
98
99     if (glyph->graphicType == HB_TAG ('d','u','p','e'))
100     {
101       if (glyph_length >= 2)
102       {
103         glyph_id = *((HBUINT16 *) &glyph->data);
104         if (retry_count--)
105           goto retry;
106       }
107       return hb_blob_get_empty ();
108     }
109
110     if (unlikely (file_type != glyph->graphicType))
111       return hb_blob_get_empty ();
112
113     if (strike_ppem) *strike_ppem = ppem;
114     if (x_offset) *x_offset = glyph->xOffset;
115     if (y_offset) *y_offset = glyph->yOffset;
116     return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length);
117   }
118
119   public:
120   HBUINT16      ppem;           /* The PPEM size for which this strike was designed. */
121   HBUINT16      resolution;     /* The device pixel density (in PPI) for which this
122                                  * strike was designed. (E.g., 96 PPI, 192 PPI.) */
123   protected:
124   UnsizedArrayOf<LOffsetTo<SBIXGlyph>>
125                 imageOffsetsZ;  /* Offset from the beginning of the strike data header
126                                  * to bitmap data for an individual glyph ID. */
127   public:
128   DEFINE_SIZE_ARRAY (4, imageOffsetsZ);
129 };
130
131 struct sbix
132 {
133   static constexpr hb_tag_t tableTag = HB_OT_TAG_sbix;
134
135   bool has_data () const { return version; }
136
137   const SBIXStrike &get_strike (unsigned int i) const { return this+strikes[i]; }
138
139   struct accelerator_t
140   {
141     void init (hb_face_t *face)
142     {
143       table = hb_sanitize_context_t().reference_table<sbix> (face);
144       num_glyphs = face->get_num_glyphs ();
145     }
146     void fini () { table.destroy (); }
147
148     bool has_data () const { return table->has_data (); }
149
150     bool get_extents (hb_font_t          *font,
151                       hb_codepoint_t      glyph,
152                       hb_glyph_extents_t *extents) const
153     {
154       /* We only support PNG right now, and following function checks type. */
155       return get_png_extents (font, glyph, extents);
156     }
157
158     hb_blob_t *reference_png (hb_font_t      *font,
159                               hb_codepoint_t  glyph_id,
160                               int            *x_offset,
161                               int            *y_offset,
162                               unsigned int   *available_ppem) const
163     {
164       return choose_strike (font).get_glyph_blob (glyph_id, table.get_blob (),
165                                                   HB_TAG ('p','n','g',' '),
166                                                   x_offset, y_offset,
167                                                   num_glyphs, available_ppem);
168     }
169
170     private:
171
172     const SBIXStrike &choose_strike (hb_font_t *font) const
173     {
174       unsigned count = table->strikes.len;
175       if (unlikely (!count))
176         return Null(SBIXStrike);
177
178       unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem);
179       if (!requested_ppem)
180         requested_ppem = 1<<30; /* Choose largest strike. */
181       /* TODO Add DPI sensitivity as well? */
182       unsigned int best_i = 0;
183       unsigned int best_ppem = table->get_strike (0).ppem;
184
185       for (unsigned int i = 1; i < count; i++)
186       {
187         unsigned int ppem = (table->get_strike (i)).ppem;
188         if ((requested_ppem <= ppem && ppem < best_ppem) ||
189             (requested_ppem > best_ppem && ppem > best_ppem))
190         {
191           best_i = i;
192           best_ppem = ppem;
193         }
194       }
195
196       return table->get_strike (best_i);
197     }
198
199     struct PNGHeader
200     {
201       HBUINT8   signature[8];
202       struct
203       {
204         struct
205         {
206           HBUINT32      length;
207           Tag           type;
208         }               header;
209         HBUINT32        width;
210         HBUINT32        height;
211         HBUINT8         bitDepth;
212         HBUINT8         colorType;
213         HBUINT8         compressionMethod;
214         HBUINT8         filterMethod;
215         HBUINT8         interlaceMethod;
216       } IHDR;
217
218       public:
219       DEFINE_SIZE_STATIC (29);
220     };
221
222     bool get_png_extents (hb_font_t          *font,
223                           hb_codepoint_t      glyph,
224                           hb_glyph_extents_t *extents) const
225     {
226       /* Following code is safe to call even without data.
227        * But faster to short-circuit. */
228       if (!has_data ())
229         return false;
230
231       int x_offset = 0, y_offset = 0;
232       unsigned int strike_ppem = 0;
233       hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
234
235       const PNGHeader &png = *blob->as<PNGHeader>();
236
237       extents->x_bearing = x_offset;
238       extents->y_bearing = png.IHDR.height + y_offset;
239       extents->width     = png.IHDR.width;
240       extents->height    = -png.IHDR.height;
241
242       /* Convert to font units. */
243       if (strike_ppem)
244       {
245         float scale = font->face->get_upem () / (float) strike_ppem;
246         extents->x_bearing = font->em_scalef_x (extents->x_bearing * scale);
247         extents->y_bearing = font->em_scalef_y (extents->y_bearing * scale);
248         extents->width = font->em_scalef_x (extents->width * scale);
249         extents->height = font->em_scalef_y (extents->height * scale);
250       }
251       else
252       {
253         extents->x_bearing = font->em_scale_x (extents->x_bearing);
254         extents->y_bearing = font->em_scale_y (extents->y_bearing);
255         extents->width = font->em_scale_x (extents->width);
256         extents->height = font->em_scale_y (extents->height);
257       }
258
259       hb_blob_destroy (blob);
260
261       return strike_ppem;
262     }
263
264     private:
265     hb_blob_ptr_t<sbix> table;
266
267     unsigned int num_glyphs;
268   };
269
270   bool sanitize (hb_sanitize_context_t *c) const
271   {
272     TRACE_SANITIZE (this);
273     return_trace (likely (c->check_struct (this) &&
274                           version >= 1 &&
275                           strikes.sanitize (c, this)));
276   }
277
278   protected:
279   HBUINT16      version;        /* Table version number — set to 1 */
280   HBUINT16      flags;          /* Bit 0: Set to 1. Bit 1: Draw outlines.
281                                  * Bits 2 to 15: reserved (set to 0). */
282   LOffsetLArrayOf<SBIXStrike>
283                 strikes;        /* Offsets from the beginning of the 'sbix'
284                                  * table to data for each individual bitmap strike. */
285   public:
286   DEFINE_SIZE_ARRAY (8, strikes);
287 };
288
289 struct sbix_accelerator_t : sbix::accelerator_t {};
290
291 } /* namespace OT */
292
293 #endif /* HB_OT_COLOR_SBIX_TABLE_HH */