1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
//! This module provides capabilities for managing a cache of rendered glyphs in GPU memory, with the goal of
//! minimisng the size and frequency of glyph uploads to GPU memory from the CPU.
//!
//! Typical applications that render directly with hardware graphics APIs (e.g. games) need text rendering.
//! There is not yet a performant solution for high quality text rendering directly on the GPU that isn't
//! experimental research work. Quality is often critical for legibility, so many applications use text or
//! individual characters that have been rendered on the CPU. This is done either ahead-of-time, giving a
//! fixed set of fonts, characters, and sizes that can be used at runtime, or dynamically as text is required.
//! This latter scenario is more flexible and the focus of this module.

//! To minimise the CPU load and texture upload bandwidth saturation, recently used glyphs should be cached
//! on the GPU for use by future frames. This module provides a mechanism for maintaining such a cache in the
//! form of a single packed 2D GPU texture. When a rendered glyph is requested, it is either retrieved from its
//! location in the texture if it is present or room is made in the cache (if necessary),
//! the CPU renders the glyph then it is uploaded into a gap in the texture to be available for GPU rendering.
//! This cache uses a Least Recently Used (LRU) cache eviction scheme - glyphs in the cache that have not been
//! used recently are as a rule of thumb not likely to be used again soon, so they are the best candidates for
//! eviction to make room for required glyphs.
//!
//! The API for the cache does not assume a particular graphics API. The intended usage is to queue up glyphs
//! that need to be present for the current frame using `Cache::queue_glyph`, update the cache to ensure that
//! the queued glyphs are present using `Cache::cache_queued` (providing a function for uploading pixel data),
//! then when it's time to render call `Cache::rect_for` to get the UV coordinates in the cache texture for
//! each glyph. For a concrete use case see the `gpu_cache` example.

use ::{PositionedGlyph, Rect, Scale, GlyphId, Vector};
use std::collections::{HashMap, HashSet, BTreeMap};
use std::collections::Bound::{Included, Unbounded};
use linked_hash_map::LinkedHashMap;

#[derive(PartialEq, PartialOrd, Copy, Clone, Debug)]
struct PGlyphSpec {
    font_id: usize,
    glyph_id: GlyphId,
    scale: Scale,
    offset: Vector<f32>
}

impl ::std::cmp::Eq for PGlyphSpec {}

impl ::std::cmp::Ord for PGlyphSpec {
    fn cmp(&self, other: &PGlyphSpec) -> ::std::cmp::Ordering {
        self.partial_cmp(other).unwrap()
    }
}

impl PGlyphSpec {
    /// Returns if this cached glyph can be considered to match another
    /// at input tolerances
    fn matches(&self, other: &PGlyphSpec, scale_tolerance: f32, position_tolerance: f32)
        -> bool
    {
        self.font_id == other.font_id &&
            self.glyph_id == other.glyph_id &&
            (self.scale.x - other.scale.x).abs() < scale_tolerance &&
            (self.scale.y - other.scale.y).abs() < scale_tolerance &&
            (self.offset.x - other.offset.x).abs() < position_tolerance &&
            (self.offset.y - other.offset.y).abs() < position_tolerance
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct ByteArray2d {
    inner_array: Vec<u8>,
    row: usize,
    col: usize,
}

impl ByteArray2d {
    pub fn zeros(row: usize, col: usize) -> Self {
        ByteArray2d {
            inner_array: vec![0; row * col],
            row: row,
            col: col,
        }
    }

    pub fn as_slice(&self) -> &[u8] {
        self.inner_array.as_slice()
    }

    fn get_vec_index(&self, row: usize, col: usize) -> usize {
        if row >= self.row {
            panic!("row out of range: row={}, given={}", self.row, row);
        } else if col >= self.col {
            panic!("column out of range: col={}, given={}", self.col, col);
        } else {
            row * self.col + col
        }
    }
}

impl ::std::ops::Index<(usize, usize)> for ByteArray2d {
    type Output = u8;

    fn index(&self, (row, col): (usize, usize)) -> &u8 {
        &self.inner_array[self.get_vec_index(row, col)]
    }
}

impl ::std::ops::IndexMut<(usize, usize)> for ByteArray2d {
    fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut u8 {
        let vec_index = self.get_vec_index(row, col);
        &mut self.inner_array[vec_index]
    }
}

struct Row {
    height: u32,
    width: u32,
    glyphs: Vec<(PGlyphSpec, Rect<u32>, ByteArray2d)>
}

/// An implementation of a dynamic GPU glyph cache. See the module documentation for more information.
pub struct Cache {
    scale_tolerance: f32,
    position_tolerance: f32,
    width: u32,
    height: u32,
    rows: LinkedHashMap<u32, Row>,
    space_start_for_end: HashMap<u32, u32>,
    space_end_for_start: HashMap<u32, u32>,
    queue: Vec<(usize, PositionedGlyph<'static>)>,
    queue_retry: bool,
    all_glyphs: BTreeMap<PGlyphSpec, (u32, u32)>
}

/// Returned from `Cache::rect_for`.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum CacheReadErr {
    /// Indicates that the requested glyph is not present in the cache
    GlyphNotCached
}
/// Returned from `Cache::cache_queued`.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum CacheWriteErr {
    /// At least one of the queued glyphs is too big to fit into the cache, even if all other glyphs are removed.
    GlyphTooLarge,
    /// Not all of the requested glyphs can fit into the cache, even if the cache is completely cleared before
    /// the attempt.
    NoRoomForWholeQueue
}
fn normalise_pixel_offset(mut offset: Vector<f32>) -> Vector<f32> {
    if offset.x > 0.5 {
        offset.x -= 1.0;
    } else if offset.x < -0.5 {
        offset.x += 1.0;
    }
    if offset.y > 0.5 {
        offset.y -= 1.0;
    } else if offset.y < -0.5 {
        offset.y += 1.0;
    }
    offset
}

impl Cache {
    /// Constructs a new cache. Note that this is just the CPU side of the cache. The GPU texture is managed
    /// by the user.
    ///
    /// `width` and `height` specify the dimensions of the 2D texture that will hold the cache contents on the
    /// GPU. This must match the dimensions of the actual texture used, otherwise `cache_queued` will try to
    /// cache into coordinates outside the bounds of the texture. If you need to change the dimensions of the
    /// cache texture (e.g. due to high cache pressure), construct a new `Cache` and discard the old one.
    ///
    /// `scale_tolerance` and `position_tolerance` specify the tolerances (maximum allowed difference)
    /// for judging whether an existing glyph
    /// in the cache is close enough to the requested glyph in scale and subpixel offset to be used in its
    /// place. Due to floating point inaccuracies that can affect user code it is not recommended to set these
    /// parameters too close to zero as effectively identical glyphs could end up duplicated in the cache.
    /// Both `scale_tolerance` and `position_tolerance` are measured in pixels. Note that since `pixel_tolerance`
    /// is a tolerance of subpixel offsets, setting it to 1.0 or higher is effectively a "don't care" option.
    /// A typical application will produce results with no perceptible inaccuracies with `scale_tolerance`
    /// and `position_tolerance` set to 0.1. Depending on the target DPI higher tolerance may be acceptable.
    ///
    /// # Panics
    ///
    /// `scale_tolerance` or `position_tolerance` are less than or equal to zero.
    pub fn new(width: u32, height: u32,
               scale_tolerance: f32, position_tolerance: f32) -> Cache {
        assert!(scale_tolerance >= 0.0);
        assert!(position_tolerance >= 0.0);
        let scale_tolerance = scale_tolerance.max(0.001);
        let position_tolerance = position_tolerance.max(0.001);
        Cache {
            scale_tolerance: scale_tolerance,
            position_tolerance: position_tolerance,
            width: width,
            height: height,
            rows: LinkedHashMap::new(),
            space_start_for_end: {let mut m = HashMap::new(); m.insert(height, 0); m},
            space_end_for_start: {let mut m = HashMap::new(); m.insert(0, height); m},
            queue: Vec::new(),
            queue_retry: false,
            all_glyphs: BTreeMap::new()
        }
    }
    /// Sets the scale tolerance for the cache. See the documentation for `Cache::new` for more information.
    ///
    /// # Panics
    ///
    /// `tolerance` is less than or equal to zero.
    pub fn set_scale_tolerance(&mut self, tolerance: f32) {
        assert!(tolerance >= 0.0);
        let tolerance = tolerance.max(0.001);
        self.scale_tolerance = tolerance;
    }
    /// Returns the current scale tolerance for the cache.
    pub fn scale_tolerance(&self) -> f32 {
        self.scale_tolerance
    }
    /// Sets the subpixel position tolerance for the cache. See the documentation for `Cache::new` for more
    /// information.
    ///
    /// # Panics
    ///
    /// `tolerance` is less than or equal to zero.
    pub fn set_position_tolerance(&mut self, tolerance: f32) {
        assert!(tolerance >= 0.0);
        let tolerance = tolerance.max(0.001);
        self.position_tolerance = tolerance;
    }
    /// Returns the current subpixel position tolerance for the cache.
    pub fn position_tolerance(&self) -> f32 {
        self.position_tolerance
    }
    /// Returns the cache texture dimensions assumed by the cache. For proper operation this should
    /// match the dimensions of the used GPU texture.
    pub fn dimensions(&self) -> (u32, u32) {
        (self.width, self.height)
    }
    /// Queue a glyph for caching by the next call to `cache_queued`. `font_id` is used to
    /// disambiguate glyphs from different fonts. The user should ensure that `font_id` is unique to the
    /// font the glyph is from.
    pub fn queue_glyph(&mut self, font_id: usize, glyph: PositionedGlyph) {
        if glyph.pixel_bounding_box().is_some() {
            self.queue.push((font_id, glyph.standalone()));
        }
    }
    /// Clears the cache. Does not affect the glyph queue.
    pub fn clear(&mut self) {
        self.rows.clear();
        self.space_end_for_start.clear();
        self.space_end_for_start.insert(0, self.height);
        self.space_start_for_end.clear();
        self.space_start_for_end.insert(self.height, 0);
        self.all_glyphs.clear();
    }
    /// Clears the glyph queue.
    pub fn clear_queue(&mut self) {
        self.queue.clear();
    }
    /// Caches the queued glyphs. If this is unsuccessful, the queue is untouched.
    /// Any glyphs cached by previous calls to this function may be removed from the cache to make
    /// room for the newly queued glyphs. Thus if you want to ensure that a glyph is in the cache,
    /// the most recently cached queue must have contained that glyph.
    ///
    /// `uploader` is the user-provided function that should perform the texture uploads to the GPU.
    /// The information provided is the rectangular region to insert the pixel data into, and the pixel data
    /// itself. This data is provided in horizontal scanline format (row major), with stride equal to the
    /// rectangle width.
    pub fn cache_queued<F: FnMut(Rect<u32>, &[u8])>(&mut self, mut uploader: F)
        -> Result<(), CacheWriteErr>
    {
        use vector;
        use point;
        let mut in_use_rows = HashSet::new();
        // tallest first gives better packing
        // can use 'sort_unstable' as order of equal elements is unimportant
        self.queue.sort_unstable_by_key(|&(_, ref glyph)| {
            -glyph.pixel_bounding_box().unwrap().height()
        });
        let mut queue_success = true;
        'per_glyph: for &(font_id, ref glyph) in &self.queue {
            // Check to see if it's already cached, or a close enough version is:
            // (Note that the search for "close enough" here is conservative - there may be
            // a close enough glyph that isn't found; identical glyphs however will always be found)
            let p = glyph.position();
            let pfract = normalise_pixel_offset(vector(p.x.fract(), p.y.fract()));
            let spec = PGlyphSpec {
                font_id: font_id,
                glyph_id: glyph.id(),
                scale: glyph.scale(),
                offset: pfract
            };
            let lower = self.all_glyphs.range((Unbounded, Included(&spec))).rev().next()
                .and_then(|(l, &(lrow, _))| {
                    if l.font_id == spec.font_id &&
                        l.glyph_id == spec.glyph_id &&
                        (l.scale.x - spec.scale.x).abs() < self.scale_tolerance &&
                        (l.scale.y - spec.scale.y).abs() < self.scale_tolerance &&
                        (spec.offset.x - l.offset.x).abs() < self.position_tolerance &&
                        (spec.offset.y - l.offset.y).abs() < self.position_tolerance
                    {
                        Some((l.scale, l.offset, lrow))
                    } else {
                        None
                    }
                });
            let upper = self.all_glyphs.range((Included(&spec), Unbounded)).next()
                .and_then(|(u, &(urow, _))| {
                    if u.font_id == spec.font_id &&
                        u.glyph_id == spec.glyph_id &&
                        (u.scale.x - spec.scale.x).abs() < self.scale_tolerance &&
                        (u.scale.y - spec.scale.y).abs() < self.scale_tolerance &&
                        (spec.offset.x - u.offset.x).abs() < self.position_tolerance &&
                        (spec.offset.y - u.offset.y).abs() < self.position_tolerance
                    {
                        Some((u.scale, u.offset, urow))
                    } else {
                        None
                    }
                });
            match (lower, upper) {
                (None, None) => {} // No match
                (None, Some((_, _, row))) |
                (Some((_, _, row)), None) => {
                    // just one match
                    self.rows.get_refresh(&row);
                    in_use_rows.insert(row);
                    continue 'per_glyph;
                }
                (Some((_, _, row1)), Some((_, _, row2))) if row1 == row2 => {
                    // two matches, but the same row
                    self.rows.get_refresh(&row1);
                    in_use_rows.insert(row1);
                    continue 'per_glyph;
                }
                (Some((scale1, offset1, row1)), Some((scale2, offset2, row2))) => {
                    // two definitely distinct matches
                    let v1 =
                        ((scale1.x - spec.scale.x) / self.scale_tolerance).abs()
                        + ((scale1.y - spec.scale.y) / self.scale_tolerance).abs()
                        + ((offset1.x - spec.offset.x) / self.position_tolerance).abs()
                        + ((offset1.y - spec.offset.y) / self.position_tolerance).abs();
                    let v2 =
                        ((scale2.x - spec.scale.x) / self.scale_tolerance).abs()
                        + ((scale2.y - spec.scale.y) / self.scale_tolerance).abs()
                        + ((offset2.x - spec.offset.x) / self.position_tolerance).abs()
                        + ((offset2.y - spec.offset.y) / self.position_tolerance).abs();
                    let row = if v1 < v2 { row1 } else { row2 };
                    self.rows.get_refresh(&row);
                    in_use_rows.insert(row);
                    continue 'per_glyph;
                }
            }
            // Not cached, so add it:
            let bb = glyph.pixel_bounding_box().unwrap();
            let (width, height) = (bb.width() as u32, bb.height() as u32);
            if width >= self.width || height >= self.height {
                return Result::Err(CacheWriteErr::GlyphTooLarge);
            }
            // find row to put the glyph in, most used rows first
            let mut row_top = None;
            for (top, row) in self.rows.iter().rev() {
                if row.height >= height && self.width - row.width >= width {
                    // found a spot on an existing row
                    row_top = Some(*top);
                    break;
                }
            }

            if row_top.is_none() {
                let mut gap = None;
                // See if there is space for a new row
                for (start, end) in self.space_end_for_start.iter() {
                    if end - start >= height {
                        gap = Some((*start, *end));
                        break;
                    }
                }
                if gap.is_none() {
                    // Remove old rows until room is available
                    while self.rows.len() > 0 {
                        // check that the oldest row isn't also in use
                        if !in_use_rows.contains(self.rows.front().unwrap().0) {
                            // Remove row
                            let (top, row) = self.rows.pop_front().unwrap();
                            for (spec, _, _) in row.glyphs {
                                self.all_glyphs.remove(&spec);
                            }
                            let (mut new_start, mut new_end) = (top, top + row.height);
                            // Update the free space maps
                            if let Some(end) = self.space_end_for_start.remove(&new_end) {
                                new_end = end;
                            }
                            if let Some(start) = self.space_start_for_end.remove(&new_start) {
                                new_start = start;
                            }
                            self.space_start_for_end.insert(new_end, new_start);
                            self.space_end_for_start.insert(new_start, new_end);
                            if new_end - new_start >= height {
                                // The newly formed gap is big enough
                                gap = Some((new_start, new_end));
                                break
                            }
                        } else {
                            // all rows left are in use
                            // try a clean insert of all needed glyphs
                            // if that doesn't work, fail
                            if self.queue_retry { // already trying a clean insert, don't do it again
                                return Err(CacheWriteErr::NoRoomForWholeQueue);
                            } else { // signal that a retry is needed
                                queue_success = false;
                                break 'per_glyph;
                            }
                        }
                    }
                }
                let (gap_start, gap_end) = gap.unwrap();
                // fill space for new row
                let new_space_start = gap_start + height;
                self.space_end_for_start.remove(&gap_start);
                if new_space_start == gap_end {
                    self.space_start_for_end.remove(&gap_end);
                } else {
                    self.space_end_for_start.insert(new_space_start, gap_end);
                    self.space_start_for_end.insert(gap_end, new_space_start);
                }
                // add the row
                self.rows.insert(gap_start, Row {
                    width: 0,
                    height: height,
                    glyphs: Vec::new()
                });
                row_top = Some(gap_start);
            }
            let row_top = row_top.unwrap();
            // calculate the target rect
            let row = self.rows.get_refresh(&row_top).unwrap();
            let rect = Rect {
                min: point(row.width, row_top),
                max: point(row.width + width, row_top + height)
            };
            // draw the glyph into main memory
            let mut pixels = ByteArray2d::zeros(height as usize, width as usize);
            glyph.draw(|x, y, v| {
                let v = ((v * 255.0) + 0.5).floor().max(0.0).min(255.0) as u8;
                pixels[(y as usize, x as usize)] = v;
            });
            // transfer
            uploader(
                rect,
                &pixels.as_slice());
            // add the glyph to the row
            row.glyphs.push((spec, rect, pixels));
            row.width += width;
            in_use_rows.insert(row_top);
            self.all_glyphs.insert(spec, (row_top, row.glyphs.len() as u32 - 1));
        }
        if queue_success {
            self.queue.clear();
            Ok(())
        } else { // clear the cache then try again
            self.clear();
            self.queue_retry = true;
            let result = self.cache_queued(uploader);
            self.queue_retry = false;
            result
        }
    }

    /// Retrieves the (floating point) texture coordinates of the quad for a glyph in the cache,
    /// as well as the pixel-space (integer) coordinates that this region should be drawn at.
    /// In the majority of cases these pixel-space coordinates should be identical to the bounding box of the
    /// input glyph. They only differ if the cache has returned a substitute glyph that is deemed close enough
    /// to the requested glyph as specified by the cache tolerance parameters.
    ///
    /// A sucessful result is `Some` if the glyph is not an empty glyph (no shape, and thus no rect to return).
    ///
    /// Ensure that `font_id` matches the `font_id` that was passed to `queue_glyph` with this `glyph`.
    pub fn rect_for<'a>(&'a self,
                        font_id: usize,
                        glyph: &PositionedGlyph)
                        -> Result<Option<(Rect<f32>, Rect<i32>)>, CacheReadErr>
    {
        use vector;
        use point;
        let glyph_bb = match glyph.pixel_bounding_box() {
            Some(bb) => bb,
            None => return Ok(None)
        };
        let target_position = glyph.position();
        let target_offset = normalise_pixel_offset(vector(target_position.x.fract(), target_position.y.fract()));
        let target_spec = PGlyphSpec {
            font_id: font_id,
            glyph_id: glyph.id(),
            scale: glyph.scale(),
            offset: target_offset
        };

        let (lower, upper) = {
            let mut left_range = self.all_glyphs.range((Unbounded, Included(&target_spec))).rev();
            let mut right_range = self.all_glyphs.range((Included(&target_spec), Unbounded));

            let mut left = left_range.next().map(|(s, &(r, i))| (s, r, i));
            let mut right = right_range.next().map(|(s, &(r, i))| (s, r, i));

            while left.is_some() || right.is_some() {
                left = left.and_then(|(spec, row, index)| {
                    if spec.matches(&target_spec, self.scale_tolerance, self.position_tolerance) {
                        Some((spec, row, index))
                    }
                    else { None }
                });
                right = right.and_then(|(spec, row, index)| {
                    if spec.matches(&target_spec, self.scale_tolerance, self.position_tolerance) {
                        Some((spec, row, index))
                    }
                    else { None }
                });

                if left.is_none() && right.is_none() {
                    // continue searching for a match
                    left = left_range.next().map(|(s, &(r, i))| (s, r, i));
                    right = right_range.next().map(|(s, &(r, i))| (s, r, i));
                }
                else { break; }
            }
            (left, right)
        };

        let (width, height) = (self.width as f32, self.height as f32);
        let (match_spec, row, index) = match (lower, upper) {
            (None, None) => return Err(CacheReadErr::GlyphNotCached),
            (Some(match_), None) |
            (None, Some(match_)) => match_, // one match
            (Some((lmatch_spec, lrow, lindex)), Some((umatch_spec, urow, uindex))) => {
                if lrow == urow && lindex == uindex {
                    // both matches are really the same one, and match the input
                    let tex_rect = self.rows[&lrow].glyphs[lindex as usize].1;
                    let uv_rect = Rect {
                        min: point(tex_rect.min.x as f32 / width, tex_rect.min.y as f32 / height),
                        max: point(tex_rect.max.x as f32 / width, tex_rect.max.y as f32 / height)
                    };
                    return Ok(Some((uv_rect, glyph_bb)))
                } else {
                    // Two close-enough matches. Figure out which is closest.
                    let l_measure =
                        ((lmatch_spec.scale.x - target_spec.scale.x) / self.scale_tolerance).abs()
                        + ((lmatch_spec.scale.y - target_spec.scale.y) / self.scale_tolerance).abs()
                        + ((lmatch_spec.offset.x - target_spec.offset.x) / self.position_tolerance).abs()
                        + ((lmatch_spec.offset.y - target_spec.offset.y) / self.position_tolerance).abs();
                    let u_measure =
                        ((umatch_spec.scale.x - target_spec.scale.x) / self.scale_tolerance).abs()
                        + ((umatch_spec.scale.y - target_spec.scale.y) / self.scale_tolerance).abs()
                        + ((umatch_spec.offset.x - target_spec.offset.x) / self.position_tolerance).abs()
                        + ((umatch_spec.offset.y - target_spec.offset.y) / self.position_tolerance).abs();
                    if l_measure < u_measure {
                        (lmatch_spec, lrow, lindex)
                    } else {
                        (umatch_spec, urow, uindex)
                    }
                }
            }
        };
        let tex_rect = self.rows[&row].glyphs[index as usize].1;
        let uv_rect = Rect {
            min: point(tex_rect.min.x as f32 / width, tex_rect.min.y as f32 / height),
            max: point(tex_rect.max.x as f32 / width, tex_rect.max.y as f32 / height)
        };
        let local_bb = glyph
            .unpositioned().clone()
            .positioned(point(0.0, 0.0) + match_spec.offset).pixel_bounding_box().unwrap();
        let min_from_origin =
            point(local_bb.min.x as f32, local_bb.min.y as f32)
                    - (point(0.0, 0.0) + match_spec.offset);
        let ideal_min = min_from_origin + target_position;
        let min = point(ideal_min.x.round() as i32, ideal_min.y.round() as i32);
        let bb_offset = min - local_bb.min;
        let bb = Rect {
            min: min,
            max: local_bb.max + bb_offset
        };
        Ok(Some((uv_rect, bb)))
    }
}

#[cfg(test)]
#[test]
fn cache_test() {
    use ::FontCollection;
    use ::Scale;
    use ::point;
    let mut cache = Cache::new(32, 32, 0.1, 0.1);
    let font_data = include_bytes!("../examples/DejaVuSans.ttf");
    let font = FontCollection::from_bytes(font_data as &[u8]).into_font().unwrap();
    let strings = [
        ("Hello World!", 15.0),
        ("Hello World!", 14.0),
        ("Hello World!", 10.0),
        ("Hello World!", 15.0),
        ("Hello World!", 14.0),
        ("Hello World!", 10.0)
            ];
    for i in 0..strings.len() {
        println!("Caching {:?}", strings[i]);
        for glyph in font.layout(strings[i].0, Scale { x: strings[i].1, y: strings[i].1 }, point(0.0, 0.0)) {
            cache.queue_glyph(0, glyph);
        }
        cache.cache_queued(|_, _| {}).unwrap();
    }
}

#[cfg(test)]
#[test]
fn need_to_check_whole_cache() {
    use ::FontCollection;
    use ::Scale;
    use ::point;
    let font_data = include_bytes!("../examples/DejaVuSans.ttf");
    let font = FontCollection::from_bytes(font_data as &[u8]).into_font().unwrap();

    let glyph = font.glyph('l').unwrap();

    let small = glyph.clone().scaled(Scale::uniform(10.0));
    let large = glyph.clone().scaled(Scale::uniform(10.05));

    let small_left = small.clone().positioned(point(0.0, 0.0));
    let large_left = large.clone().positioned(point(0.0, 0.0));
    let large_right = large.clone().positioned(point(-0.2, 0.0));

    let mut cache = Cache::new(32, 32, 0.1, 0.1);

    cache.queue_glyph(0, small_left.clone());
    cache.queue_glyph(0, large_left.clone()); // Noop since it's within the scale tolerance of small_left
    cache.queue_glyph(0, large_right.clone());

    cache.cache_queued(|_, _| {}).unwrap();

    cache.rect_for(0, &small_left).unwrap();
    cache.rect_for(0, &large_left).unwrap();
    cache.rect_for(0, &large_right).unwrap();
}

#[cfg(feature = "bench")]
#[cfg(test)]
mod cache_bench_tests {
    use super::*;
    use ::{FontCollection, Scale, Font, point};

    const FONT_ID: usize = 0;
    // TODO use a more complex font
    const FONT_BYTES: &[u8] = include_bytes!("../examples/DejaVuSans.ttf");
    const TEST_STR: &str = include_str!("../tests/lipsum.txt");

    /// Reproduces Err(GlyphNotCached) issue & serves as a general purpose cache benchmark
    #[bench]
    fn cache_bench_tolerance_p1(b: &mut ::test::Bencher) {
        let glyphs = test_glyphs();
        let mut cache = Cache::new(768, 768, 0.1, 0.1);

        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(FONT_ID, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(FONT_ID, glyph);
                assert!(rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                        rect, index, glyph.id().0);
            }
        });
    }

    #[bench]
    fn cache_bench_tolerance_1(b: &mut ::test::Bencher) {
        let glyphs = test_glyphs();
        let mut cache = Cache::new(768, 768, 0.1, 1.0);

        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(FONT_ID, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(FONT_ID, glyph);
                assert!(rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                        rect, index, glyph.id().0);
            }
        });
    }

    fn test_glyphs() -> Vec<PositionedGlyph<'static>> {
        let font = FontCollection::from_bytes(FONT_BYTES).into_font().unwrap();
        let mut glyphs = vec![];
        // Set of scales, found through brute force, to reproduce GlyphNotCached issue
        // Cache settings also affect this, it occurs when position_tolerance is < 1.0
        for scale in &[25_f32, 24.5, 25.01, 24.7, 24.99] {
            for glyph in layout_paragraph(&font, Scale::uniform(*scale), 500, TEST_STR) {
                glyphs.push(glyph.standalone());
            }
        }
        glyphs
    }

    fn layout_paragraph<'a>(font: &'a Font,
                            scale: Scale,
                            width: u32,
                            text: &str) -> Vec<PositionedGlyph<'a>> {
        use unicode_normalization::UnicodeNormalization;
        let mut result = Vec::new();
        let v_metrics = font.v_metrics(scale);
        let advance_height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap;
        let mut caret = point(0.0, v_metrics.ascent);
        let mut last_glyph_id = None;
        for c in text.nfc() {
            if c.is_control() {
                match c {
                    '\n' => { caret = point(0.0, caret.y + advance_height) },
                    _ => {}
                }
                continue;
            }
            let base_glyph = if let Some(glyph) = font.glyph(c) {
                glyph
            } else {
                continue;
            };
            if let Some(id) = last_glyph_id.take() {
                caret.x += font.pair_kerning(scale, id, base_glyph.id());
            }
            last_glyph_id = Some(base_glyph.id());
            let mut glyph = base_glyph.scaled(scale).positioned(caret);
            if let Some(bb) = glyph.pixel_bounding_box() {
                if bb.max.x > width as i32 {
                    caret = point(0.0, caret.y + advance_height);
                    glyph = glyph.into_unpositioned().positioned(caret);
                    last_glyph_id = None;
                }
            }
            caret.x += glyph.unpositioned().h_metrics().advance_width;
            result.push(glyph);
        }
        result
    }
}