skia2/tools/SkMetaData.cpp
Ben Wagner 1968473fea Move fontmgr_bounds labels to tight bounds
The font bounds may be empty, so label the tight bounds instead,
especially since the labels are of the glyph id that touches that edge
of the tight bounds. Also rotate the labels so they dont' run into each
other.

This also fixes SkMetaData::set so that changing an existing value
doesn't cause strange issues with iterators or attempt to use data from
the previous rec after it's been freed. (Found by running viewer in a
asan build.)

Change-Id: Id255beff5d05310f098bd14baf0935e5fd349e7e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/312494
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
2020-08-24 15:02:44 +00:00

253 lines
6.1 KiB
C++

/*
* Copyright 2006 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "tools/SkMetaData.h"
#include "include/private/SkMalloc.h"
#include "include/private/SkTo.h"
void SkMetaData::reset()
{
Rec* rec = fRec;
while (rec) {
Rec* next = rec->fNext;
Rec::Free(rec);
rec = next;
}
fRec = nullptr;
}
void SkMetaData::setS32(const char name[], int32_t value)
{
(void)this->set(name, &value, sizeof(int32_t), kS32_Type, 1);
}
void SkMetaData::setScalar(const char name[], SkScalar value)
{
(void)this->set(name, &value, sizeof(SkScalar), kScalar_Type, 1);
}
SkScalar* SkMetaData::setScalars(const char name[], int count, const SkScalar values[])
{
SkASSERT(count > 0);
if (count > 0)
return (SkScalar*)this->set(name, values, sizeof(SkScalar), kScalar_Type, count);
return nullptr;
}
void SkMetaData::setPtr(const char name[], void* ptr) {
(void)this->set(name, &ptr, sizeof(void*), kPtr_Type, 1);
}
void SkMetaData::setBool(const char name[], bool value)
{
(void)this->set(name, &value, sizeof(bool), kBool_Type, 1);
}
void* SkMetaData::set(const char name[], const void* data, size_t dataSize, Type type, int count)
{
SkASSERT(name);
SkASSERT(dataSize);
SkASSERT(count > 0);
FindResult result = this->findWithPrev(name, type);
Rec* rec;
bool reuseRec = result.rec &&
result.rec->fDataLen == dataSize &&
result.rec->fDataCount == count;
if (reuseRec) {
rec = result.rec;
} else {
size_t len = strlen(name);
rec = Rec::Alloc(sizeof(Rec) + dataSize * count + len + 1);
rec->fType = SkToU8(type);
rec->fDataLen = SkToU8(dataSize);
rec->fDataCount = SkToU16(count);
memcpy(rec->name(), name, len + 1);
}
if (data) {
memcpy(rec->data(), data, dataSize * count);
}
if (reuseRec) {
// Do nothing, reused
} else if (result.rec) {
// Had one, but had to create a new one. Invalidates iterators.
// Delayed removal since name or data may have been in the result.rec.
this->remove(result);
if (result.prev) {
rec->fNext = result.prev->fNext;
result.prev->fNext = rec;
}
} else {
// Adding a new one, stick it at head.
rec->fNext = fRec;
fRec = rec;
}
return rec->data();
}
bool SkMetaData::findS32(const char name[], int32_t* value) const
{
const Rec* rec = this->find(name, kS32_Type);
if (rec)
{
SkASSERT(rec->fDataCount == 1);
if (value)
*value = *(const int32_t*)rec->data();
return true;
}
return false;
}
bool SkMetaData::findScalar(const char name[], SkScalar* value) const
{
const Rec* rec = this->find(name, kScalar_Type);
if (rec)
{
SkASSERT(rec->fDataCount == 1);
if (value)
*value = *(const SkScalar*)rec->data();
return true;
}
return false;
}
const SkScalar* SkMetaData::findScalars(const char name[], int* count, SkScalar values[]) const
{
const Rec* rec = this->find(name, kScalar_Type);
if (rec)
{
if (count)
*count = rec->fDataCount;
if (values)
memcpy(values, rec->data(), rec->fDataCount * rec->fDataLen);
return (const SkScalar*)rec->data();
}
return nullptr;
}
bool SkMetaData::findPtr(const char name[], void** ptr) const {
const Rec* rec = this->find(name, kPtr_Type);
if (rec) {
SkASSERT(rec->fDataCount == 1);
void** found = (void**)rec->data();
if (ptr) {
*ptr = *found;
}
return true;
}
return false;
}
bool SkMetaData::findBool(const char name[], bool* value) const
{
const Rec* rec = this->find(name, kBool_Type);
if (rec)
{
SkASSERT(rec->fDataCount == 1);
if (value)
*value = *(const bool*)rec->data();
return true;
}
return false;
}
SkMetaData::FindResult SkMetaData::findWithPrev(const char name[], Type type) const {
FindResult current { fRec, nullptr };
while (current.rec) {
if (current.rec->fType == type && !strcmp(current.rec->name(), name))
return current;
current.prev = current.rec;
current.rec = current.rec->fNext;
}
return current;
}
const SkMetaData::Rec* SkMetaData::find(const char name[], Type type) const {
return this->findWithPrev(name, type).rec;
}
void SkMetaData::remove(FindResult result) {
SkASSERT(result.rec);
if (result.prev) {
result.prev->fNext = result.rec->fNext;
} else {
fRec = result.rec->fNext;
}
Rec::Free(result.rec);
}
bool SkMetaData::remove(const char name[], Type type) {
FindResult result = this->findWithPrev(name, type);
if (!result.rec) {
return false;
}
this->remove(result);
return true;
}
bool SkMetaData::removeS32(const char name[])
{
return this->remove(name, kS32_Type);
}
bool SkMetaData::removeScalar(const char name[])
{
return this->remove(name, kScalar_Type);
}
bool SkMetaData::removePtr(const char name[])
{
return this->remove(name, kPtr_Type);
}
bool SkMetaData::removeBool(const char name[])
{
return this->remove(name, kBool_Type);
}
///////////////////////////////////////////////////////////////////////////////
SkMetaData::Iter::Iter(const SkMetaData& metadata) {
fRec = metadata.fRec;
}
void SkMetaData::Iter::reset(const SkMetaData& metadata) {
fRec = metadata.fRec;
}
const char* SkMetaData::Iter::next(SkMetaData::Type* t, int* count) {
const char* name = nullptr;
if (fRec) {
if (t) {
*t = (SkMetaData::Type)fRec->fType;
}
if (count) {
*count = fRec->fDataCount;
}
name = fRec->name();
fRec = fRec->fNext;
}
return name;
}
///////////////////////////////////////////////////////////////////////////////
SkMetaData::Rec* SkMetaData::Rec::Alloc(size_t size) {
return (Rec*)sk_malloc_throw(size);
}
void SkMetaData::Rec::Free(Rec* rec) {
sk_free(rec);
}