Merge pull request #21097 from hrydgard/more-assorted-fixes

A crashfix and some cleanup
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-01-03 07:00:05 +01:00
commit ef8419288b
15 files changed
+235 -195

No files matched your search

+4 -3
View File
@@ -6,6 +6,7 @@
#include <functional>
#include <set>
#include <string>
#include <string_view>
#include <mutex>
#include <queue>
#include <condition_variable>
@@ -83,6 +84,7 @@ private:
class GLRShader {
public:
explicit GLRShader(std::string_view _desc) : desc(_desc) {}
~GLRShader() {
if (shader) {
glDeleteShader(shader);
@@ -275,11 +277,10 @@ public:
return step.create_buffer.buffer;
}
GLRShader *CreateShader(GLuint stage, const std::string &code, const std::string &desc) {
GLRShader *CreateShader(GLuint stage, const std::string &code, std::string_view desc) {
GLRInitStep &step = initSteps_.push_uninitialized();
step.stepType = GLRInitStepType::CREATE_SHADER;
step.create_shader.shader = new GLRShader();
step.create_shader.shader->desc = desc;
step.create_shader.shader = new GLRShader(desc);
step.create_shader.stage = stage;
step.create_shader.code = new char[code.size() + 1];
memcpy(step.create_shader.code, code.data(), code.size() + 1);
+7 -1
View File
@@ -567,7 +567,7 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
caps_.fragmentShaderInt32Supported = true;
}
}
caps_.texture3DSupported = gl_extensions.OES_texture_3D;
caps_.texture3DSupported = gl_extensions.GLES3 || gl_extensions.OES_texture_3D;
caps_.textureDepthSupported = gl_extensions.GLES3 || gl_extensions.OES_depth_texture;
} else {
if (gl_extensions.VersionGEThan(3, 3, 0)) {
@@ -1310,6 +1310,12 @@ bool OpenGLPipeline::LinkShaders(const PipelineDesc &desc) {
}
}
if (linkShaders.empty()) {
// Can't proceed.
ERROR_LOG(Log::G3D, "LinkShaders: No valid shaders to link");
return false;
}
std::vector<GLRProgram::Semantic> semantics;
semantics.reserve(8);
// Bind all the common vertex data points. Mismatching ones will be ignored.
+5
View File
@@ -1225,6 +1225,11 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
for (auto &iter : desc.shaders) {
VKShaderModule *vkshader = (VKShaderModule *)iter;
if (!iter) {
ERROR_LOG(Log::G3D, "Null shader in pipeline creation");
delete pipeline;
return nullptr;
}
vkshader->AddRef();
pipeline->deps.push_back(vkshader);
if (vkshader->GetStage() == ShaderStage::Vertex) {
+8 -3
View File
@@ -25,7 +25,7 @@ TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw)
method_allocFont = env->GetStaticMethodID(cls_textRenderer, "allocFont", "(Ljava/lang/String;)I");
method_freeAllFonts = env->GetStaticMethodID(cls_textRenderer, "freeAllFonts", "()V");
method_measureText = env->GetStaticMethodID(cls_textRenderer, "measureText", "(Ljava/lang/String;ID)I");
method_renderText = env->GetStaticMethodID(cls_textRenderer, "renderText", "(Ljava/lang/String;ID)[I");
method_renderText = env->GetStaticMethodID(cls_textRenderer, "renderText", "(Ljava/lang/String;IDII)[I");
} else {
ERROR_LOG(Log::G3D, "Failed to find class: '%s'", textRendererClassName);
}
@@ -126,7 +126,7 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextS
// WARN_LOG(Log::G3D, "Text: '%.*s' (%02x)", (int)str.length(), str.data(), str[0]);
// TODO: Handle bold/italic
jintArray imageData = (jintArray)env->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, iter->second.font, iter->second.size);
jintArray imageData = (jintArray)env->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, iter->second.font, iter->second.size, imageWidth, imageHeight);
env->DeleteLocalRef(jstr);
entry.texture = nullptr;
@@ -137,7 +137,12 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextS
entry.lastUsedFrame = frameCount_;
jint *jimage = env->GetIntArrayElements(imageData, nullptr);
_assert_(env->GetArrayLength(imageData) == imageWidth * imageHeight);
if (env->GetArrayLength(imageData) != imageWidth * imageHeight) {
ERROR_LOG(Log::G3D, "TextRenderer returned bad image size");
env->ReleaseIntArrayElements(imageData, jimage, JNI_ABORT);
env->DeleteLocalRef(imageData);
return false;
}
if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) {
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t));
uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0];
+6 -1
View File
@@ -571,7 +571,12 @@ void __DisplaySetDisplayLayoutConfig(const DisplayLayoutConfig &config) {
}
void __DisplayFlip(int cyclesLate) {
_dbg_assert_(gpu);
if (!gpu) {
_dbg_assert_(gpu);
// Something has gone wrong.
flippedThisFrame = true;
return;
}
__DisplaySetFramerate();
+1 -2
View File
@@ -596,8 +596,7 @@ Draw::ShaderModule *PresentationCommon::CompileShaderModule(ShaderStage stage, S
return nullptr;
}
}
Draw::ShaderModule *shader = draw_->CreateShaderModule(stage, lang_, (const uint8_t *)translated.c_str(), translated.size(), "postshader");
return shader;
return draw_->CreateShaderModule(stage, lang_, (const uint8_t *)translated.c_str(), translated.size(), "postshader");
}
void PresentationCommon::SourceTexture(Draw::Texture *texture, int bufferWidth, int bufferHeight) {
+1
View File
@@ -248,6 +248,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
if (plan.depth == 1) {
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D, tw, th, 1, plan.levelsToCreate);
} else {
_dbg_assert_(draw_->GetDeviceCaps().texture3DSupported);
entry->textureName = render_->CreateTexture(GL_TEXTURE_3D, tw, th, plan.depth, 1);
}
+1
View File
@@ -1252,6 +1252,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
break;
}
// Keep going if these commands don't change state.
case GE_CMD_TEXBUFWIDTH0:
case GE_CMD_TEXADDR0:
if (data != gstate.cmdmem[data >> 24])
+4 -5
View File
@@ -682,7 +682,10 @@ public:
ShaderDepalMode shaderDepalMode;
GEBufferFormat depalFramebufferFormat;
u32 getRelativeAddress(u32 data) const;
u32 getRelativeAddress(u32 data) const {
u32 baseExtended = ((gstate.base & 0x000F0000) << 8) | data;
return (offsetAddr + baseExtended) & 0x0FFFFFFF;
}
static void Reset();
void DoState(PointerWrap &p);
};
@@ -692,7 +695,3 @@ class GPUDebugInterface;
extern GPUStateCache gstate_c;
inline u32 GPUStateCache::getRelativeAddress(u32 data) const {
u32 baseExtended = ((gstate.base & 0x000F0000) << 8) | data;
return (gstate_c.offsetAddr + baseExtended) & 0x0FFFFFFF;
}
+158 -152
View File
@@ -1,180 +1,186 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Android Crash Dump Cleaner</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<style>
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
margin: 18px;
}
<meta charset="utf-8" />
<title>Android Crash Dump Cleaner</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<style>
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
margin: 18px;
}
h1 {
margin: 0 0 12px 0;
font-size: 20px;
}
h1 {
margin: 0 0 12px 0;
font-size: 20px;
}
.row {
display: flex;
gap: 12px;
}
.row {
display: flex;
gap: 12px;
}
textarea {
width: 100%;
min-height: 320px;
resize: vertical;
padding: 10px;
font-family: ui-monospace, SFMono-Regular, Menlo, "Roboto Mono", monospace;
font-size: 13px;
line-height: 1.4;
}
textarea {
width: 100%;
min-height: 320px;
resize: vertical;
padding: 10px;
font-family: ui-monospace, SFMono-Regular, Menlo, "Roboto Mono", monospace;
font-size: 13px;
line-height: 1.4;
}
.controls {
margin: 10px 0 14px 0;
display: flex;
gap: 8px;
align-items: center;
}
.controls {
margin: 10px 0 14px 0;
display: flex;
gap: 8px;
align-items: center;
}
button {
padding: 8px 12px;
border-radius: 6px;
border: 1px solid #bbb;
background: #f6f6f6;
cursor: pointer;
}
button {
padding: 8px 12px;
border-radius: 6px;
border: 1px solid #bbb;
background: #f6f6f6;
cursor: pointer;
}
button:active {
transform: translateY(1px);
}
button:active {
transform: translateY(1px);
}
.small {
font-size: 12px;
color: #555;
}
.small {
font-size: 12px;
color: #555;
}
.col {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
.col {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
label {
font-size: 13px;
margin-bottom: 4px;
}
label {
font-size: 13px;
margin-bottom: 4px;
}
.top-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
</style>
.top-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
</style>
</head>
<body>
<h1>Android crash dump cleaner</h1>
<div class="row">
<div class="col">
<label for="input">Input (paste crash dump here)</label>
<textarea id="input" placeholder="Paste Android backtrace here..."></textarea>
<div class="controls">
<div class="top-actions">
<button id="convert">Convert →</button>
<button id="clearInput">Clear input</button>
<button id="example">Insert example</button>
</div>
<div style="margin-left:auto" class="small">Automatically removes BuildId and shortens paths to the arch + filename.</div>
</div>
</div>
<h1>Android crash dump cleaner</h1>
<div class="row">
<div class="col">
<label for="input">Input (paste crash dump here)</label>
<textarea id="input" placeholder="Paste Android backtrace here..."></textarea>
<div class="controls">
<div class="top-actions">
<button id="convert">Convert →</button>
<button id="clearInput">Clear input</button>
<button id="example">Insert example</button>
</div>
<div style="margin-left:auto" class="small">Automatically removes BuildId and shortens paths to the arch
+ filename.</div>
</div>
</div>
<div class="col">
<label for="output">Output (copy from here)</label>
<textarea id="output" readonly placeholder="Converted output will appear here..."></textarea>
<div class="controls">
<button id="copy">Copy output</button>
<button id="clearOutput">Clear output</button>
<div style="margin-left:auto" class="small" id="copystatus"></div>
</div>
</div>
</div>
<div class="col">
<label for="output">Output (copy from here)</label>
<textarea id="output" readonly placeholder="Converted output will appear here..."></textarea>
<div class="controls">
<button id="copy">Copy output</button>
<button id="clearOutput">Clear output</button>
<div style="margin-left:auto" class="small" id="copystatus"></div>
</div>
</div>
</div>
<script>
// Core cleaning function
function cleanCrashDump(text) {
// Split lines and process each.
const lines = text.split(/\r?\n/);
const archPattern = /(\/(?:arm64|arm|aarch64|x86_64|x86)\/[^)\s]+)/i;
const buildIdPattern = /\s*\(BuildId:[^)]+\)\s*$/g;
<script>
function cleanCrashDump(text) {
const lines = text.split(/\r?\n/);
const archPattern = /(\/(?:arm64|arm|aarch64|x86_64|x86)\/[^)\s]+)/i;
const buildIdPattern = /\s*\(BuildId:[^)]+\)\s*$/g;
const pcPattern = /(pc\s+0x)([0-9a-fA-F]+)/;
return lines.map(line => {
// 1) Remove trailing "(BuildId: ...)" occurrences
line = line.replace(buildIdPattern, '');
return lines.map(line => {
// 1) Remove trailing BuildId
line = line.replace(buildIdPattern, '');
// 2) Find an architecture-based path inside the line (like /arm64/libfoo.so or /arm/libfoo.so)
// If found, replace the long absolute path (the first slash..until space or '(') with that arch-based substring.
const archMatch = line.match(archPattern);
if (archMatch) {
// Replace the first full absolute path (starting with slash up to space or '(') with the short archMatch[0].
// This aims to turn: /data/app/.../lib/arm64/libppsspp_jni.so => /arm64/libppsspp_jni.so
line = line.replace(/\/[^()\s]+/, archMatch[0]);
}
// 1.5) Shorten pc to 32-bit
line = line.replace(pcPattern, (_, prefix, hex) => {
return prefix + hex.slice(-8);
});
return line;
}).join("\n");
}
// 2) Shorten absolute paths to /arch/libfoo.so
const archMatch = line.match(archPattern);
if (archMatch) {
line = line.replace(/\/[^()\s]+/, archMatch[0]);
}
// UI wiring
const inputEl = document.getElementById('input');
const outputEl = document.getElementById('output');
const convertBtn = document.getElementById('convert');
const copyBtn = document.getElementById('copy');
const clearInputBtn = document.getElementById('clearInput');
const clearOutputBtn = document.getElementById('clearOutput');
const exampleBtn = document.getElementById('example');
const copyStatus = document.getElementById('copystatus');
return line;
}).join("\n");
}
convertBtn.addEventListener('click', () => {
const inText = inputEl.value;
outputEl.value = cleanCrashDump(inText);
copyStatus.textContent = '';
});
copyBtn.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(outputEl.value);
copyStatus.textContent = 'Copied ✓';
setTimeout(() => { copyStatus.textContent = ''; }, 1500);
} catch (e) {
copyStatus.textContent = 'Copy failed';
console.error(e);
}
});
// UI wiring
const inputEl = document.getElementById('input');
const outputEl = document.getElementById('output');
const convertBtn = document.getElementById('convert');
const copyBtn = document.getElementById('copy');
const clearInputBtn = document.getElementById('clearInput');
const clearOutputBtn = document.getElementById('clearOutput');
const exampleBtn = document.getElementById('example');
const copyStatus = document.getElementById('copystatus');
clearInputBtn.addEventListener('click', () => { inputEl.value = ''; });
clearOutputBtn.addEventListener('click', () => { outputEl.value = ''; copyStatus.textContent = ''; });
convertBtn.addEventListener('click', () => {
const inText = inputEl.value;
outputEl.value = cleanCrashDump(inText);
copyStatus.textContent = '';
});
exampleBtn.addEventListener('click', () => {
inputEl.value = `backtrace:
#00 pc 0x0000000001310974 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (ff_seek_frame_binary+360) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#01 pc 0x0000000001311700 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (av_seek_frame+572) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#02 pc 0x0000000000ab4e9c /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (MediaEngine::openContext(bool)+760) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#03 pc 0x0000000000ab5be4 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (MediaEngine::addStreamData(unsigned char const*, int)+388) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#04 pc 0x0000000000a1b048 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (PostPutAction::run(MipsCall&)+1072) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#05 pc 0x000000000096b7cc /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (HLEReturnFromMipsCall()+652) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#06 pc 0x000000000096bfd8 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (CallSyscallWithoutFlags(HLEFunction const*)) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#07 pc 0x00000000000c2068`;
});
copyBtn.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(outputEl.value);
copyStatus.textContent = 'Copied ✓';
setTimeout(() => { copyStatus.textContent = ''; }, 1500);
} catch (e) {
copyStatus.textContent = 'Copy failed';
console.error(e);
}
});
// Optional: convert on paste/drop for convenience
inputEl.addEventListener('paste', (e) => {
// small convenience: automatically convert after paste
setTimeout(() => {
outputEl.value = cleanCrashDump(inputEl.value);
}, 50);
});
clearInputBtn.addEventListener('click', () => { inputEl.value = ''; });
clearOutputBtn.addEventListener('click', () => { outputEl.value = ''; copyStatus.textContent = ''; });
</script>
exampleBtn.addEventListener('click', () => {
inputEl.value = `backtrace:
#00 pc 0x0000000001310974 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (ff_seek_frame_binary+360) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#01 pc 0x0000000001311700 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (av_seek_frame+572) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#02 pc 0x0000000000ab4e9c /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (MediaEngine::openContext(bool)+760) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#03 pc 0x0000000000ab5be4 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (MediaEngine::addStreamData(unsigned char const*, int)+388) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#04 pc 0x0000000000a1b048 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (PostPutAction::run(MipsCall&)+1072) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#05 pc 0x000000000096b7cc /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (HLEReturnFromMipsCall()+652) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#06 pc 0x000000000096bfd8 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (CallSyscallWithoutFlags(HLEFunction const*)) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
#07 pc 0x00000000000c2068`;
});
// Optional: convert on paste/drop for convenience
inputEl.addEventListener('paste', (e) => {
// small convenience: automatically convert after paste
setTimeout(() => {
outputEl.value = cleanCrashDump(inputEl.value);
}, 50);
});
</script>
</body>
</html>
</html>
+8
View File
@@ -935,6 +935,14 @@ void GameInfoCache::Clear() {
CancelAll();
std::lock_guard<std::mutex> lock(mapLock_);
// NOTE: Some shared_pointers might have other owners. We still need to wipe their textures here.
for (auto &[key, value] : info_) {
std::lock_guard<std::mutex> lock(value->lock);
value->pic0.Clear();
value->pic1.Clear();
value->icon.Clear();
value->hasFlags &= ~(GameInfoFlags::PIC0 | GameInfoFlags::PIC1 | GameInfoFlags::ICON);
}
info_.clear();
}
+2
View File
@@ -185,6 +185,8 @@ GameInfoTex *GameImageView::GetTex(std::shared_ptr<GameInfo> info) const {
case GameInfoFlags::PIC1:
tex = &info->pic1;
break;
default:
break;
}
return tex;
}
-1
View File
@@ -104,6 +104,5 @@ private:
UI::ViewGroup *scrollItemView_ = nullptr;
UI::ViewGroup *productPanel_ = nullptr;
UI::TextView *titleText_ = nullptr;
};
@@ -116,16 +116,16 @@ public class ShortcutActivity extends Activity {
PpssppActivity.CheckABIAndLoadLibrary();
String gameName = name;
byte[] iconData = null;
byte[] iconData;
Object[] result = queryGameInfo(this, path);
if (result != null && result.length >= 2) {
if (result[0] != null) {
gameName = (String) result[0]; // index 0 = name
}
iconData = (byte[]) result[1]; // index 1 = raw PNG/JPEG bytes, or null
Log.i(TAG, "Game name: " + gameName);
} else {
iconData = null;
Log.e(TAG, "Bad return value from queryGameInfo");
}
@@ -138,29 +138,37 @@ public class ShortcutActivity extends Activity {
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
if (iconData != null) {
// Try to create a PNG from the iconData.
Bitmap bmp = BitmapFactory.decodeByteArray(iconData, 0, iconData.length);
if (bmp != null) {
// Pad the bitmap into a square, to keep it a nice 2:1 aspect ratio.
Bitmap paddedBitmap = Bitmap.createBitmap(
bmp.getWidth(), bmp.getWidth(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(paddedBitmap);
canvas.drawARGB(0, 0, 0, 0);
int y = (bmp.getWidth() - bmp.getHeight()) / 2;
if (y < 0) {
// To be safe from wacky-aspect-ratio bitmaps.
y = 0;
try {
// Try to create a PNG from the iconData.
Bitmap bmp = BitmapFactory.decodeByteArray(iconData, 0, iconData.length);
if (bmp != null) {
// Pad the bitmap into a square, to keep it a nice 2:1 aspect ratio.
Bitmap paddedBitmap = Bitmap.createBitmap(
bmp.getWidth(), bmp.getWidth(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(paddedBitmap);
canvas.drawARGB(0, 0, 0, 0);
int y = (bmp.getWidth() - bmp.getHeight()) / 2;
if (y < 0) {
// To be safe from wacky-aspect-ratio bitmaps.
y = 0;
}
canvas.drawBitmap(bmp, 0, y, new Paint(Paint.FILTER_BITMAP_FLAG));
// Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp, 144, 72, true);
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, paddedBitmap);
}
canvas.drawBitmap(bmp, 0, y, new Paint(Paint.FILTER_BITMAP_FLAG));
// Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp, 144, 72, true);
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, paddedBitmap);
} catch (Exception e) {
NativeApp.reportException(e, "Error assigning generated icon");
}
} else {
Log.i(TAG, "No icon available, falling back to PPSSPP icon");
// Fall back to the PPSSPP icon.
ShortcutIconResource iconResource = ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher);
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
try {
// Fall back to the PPSSPP icon.
ShortcutIconResource iconResource = ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher);
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
} catch (Exception e) {
NativeApp.reportException(e, "Error assigning default icon");
}
}
setResult(RESULT_OK, responseIntent);
@@ -109,14 +109,9 @@ public class TextRenderer {
Point s = measure(string, font, textSize);
return (s.x << 16) | s.y;
}
public static int[] renderText(String string, int font, double textSize) {
public static int[] renderText(String string, int font, double textSize, int w, int h) {
textPaint.setTypeface(fontMap.get(font));
textPaint.setTextSize((float) textSize);
Point s = measure(string, font, textSize);
int w = s.x;
int h = s.y;
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);