static void stinger_video_render(void *data, gs_effect_t *effect) { struct stinger_info *s = data; float t = obs_transition_get_time(s->source); bool use_a = t < s->transition_point; enum obs_transition_target target = use_a ? OBS_TRANSITION_SOURCE_A : OBS_TRANSITION_SOURCE_B; if (!obs_transition_video_render_direct(s->source, target)) return; /* --------------------- */ float source_cx = (float)obs_source_get_width(s->source); float source_cy = (float)obs_source_get_height(s->source); uint32_t media_cx = obs_source_get_width(s->media_source); uint32_t media_cy = obs_source_get_height(s->media_source); if (!media_cx || !media_cy) return; float scale_x = source_cx / (float)media_cx; float scale_y = source_cy / (float)media_cy; gs_matrix_push(); gs_matrix_scale3f(scale_x, scale_y, 1.0f); obs_source_video_render(s->media_source); gs_matrix_pop(); UNUSED_PARAMETER(effect); }
void OBSBasicTransform::OnControlChanged() { if (ignoreItemChange) return; obs_source_t *source = obs_sceneitem_get_source(item); double width = double(obs_source_get_width(source)); double height = double(obs_source_get_height(source)); obs_transform_info oti; oti.pos.x = float(ui->positionX->value()); oti.pos.y = float(ui->positionY->value()); oti.rot = float(ui->rotation->value()); oti.scale.x = float(ui->sizeX->value() / width); oti.scale.y = float(ui->sizeY->value() / height); oti.alignment = listToAlign[ui->align->currentIndex()]; oti.bounds_type = (obs_bounds_type)ui->boundsType->currentIndex(); oti.bounds_alignment = listToAlign[ui->boundsAlign->currentIndex()]; oti.bounds.x = float(ui->boundsWidth->value()); oti.bounds.y = float(ui->boundsHeight->value()); ignoreTransformSignal = true; obs_sceneitem_set_info(item, &oti); ignoreTransformSignal = false; }
void OBSBasicTransform::OnBoundsType(int index) { if (index == -1) return; obs_bounds_type type = (obs_bounds_type)index; bool enable = (type != OBS_BOUNDS_NONE); ui->boundsAlign->setEnabled(enable); ui->boundsWidth->setEnabled(enable); ui->boundsHeight->setEnabled(enable); if (!ignoreItemChange) { obs_bounds_type lastType = obs_sceneitem_get_bounds_type(item); if (lastType == OBS_BOUNDS_NONE) { OBSSource source = obs_sceneitem_get_source(item); int width = (int)obs_source_get_width(source); int height = (int)obs_source_get_height(source); ui->boundsWidth->setValue(width); ui->boundsHeight->setValue(height); } } OnControlChanged(); }
void OBSBasicTransform::RefreshControls() { if (!item) return; obs_transform_info osi; obs_sceneitem_get_info(item, &osi); obs_source_t *source = obs_sceneitem_get_source(item); float width = float(obs_source_get_width(source)); float height = float(obs_source_get_height(source)); int alignIndex = AlignToList(osi.alignment); int boundsAlignIndex = AlignToList(osi.bounds_alignment); ignoreItemChange = true; ui->positionX->setValue(osi.pos.x); ui->positionY->setValue(osi.pos.y); ui->rotation->setValue(osi.rot); ui->sizeX->setValue(osi.scale.x * width); ui->sizeY->setValue(osi.scale.y * height); ui->align->setCurrentIndex(alignIndex); ui->boundsType->setCurrentIndex(int(osi.bounds_type)); ui->boundsAlign->setCurrentIndex(boundsAlignIndex); ui->boundsWidth->setValue(osi.bounds.x); ui->boundsHeight->setValue(osi.bounds.y); ignoreItemChange = false; }
static inline void render_item(struct obs_scene_item *item) { if (item->crop_render) { uint32_t width = obs_source_get_width(item->source); uint32_t height = obs_source_get_height(item->source); uint32_t cx = calc_cx(item, width); uint32_t cy = calc_cy(item, height); if (cx && cy && gs_texrender_begin(item->crop_render, cx, cy)) { float cx_scale = (float)width / (float)cx; float cy_scale = (float)height / (float)cy; gs_matrix_scale3f(cx_scale, cy_scale, 1.0f); gs_matrix_translate3f( -(float)item->crop.left, -(float)item->crop.top, 0.0f); obs_source_video_render(item->source); gs_texrender_end(item->crop_render); } } gs_matrix_push(); gs_matrix_mul(&item->draw_transform); if (item->crop_render) { gs_texture_t *tex = gs_texrender_get_texture(item->crop_render); while (gs_effect_loop(obs->video.default_effect, "Draw")) obs_source_draw(tex, 0, 0, 0, 0, 0); } else { obs_source_video_render(item->source); } gs_matrix_pop(); }
static inline bool source_size_changed(struct obs_scene_item *item) { uint32_t width = obs_source_get_width(item->source); uint32_t height = obs_source_get_height(item->source); return item->last_width != width || item->last_height != height; }
void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) { OBSProjector *window = reinterpret_cast<OBSProjector*>(data); if (!window->ready) return; OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow()); OBSSource source = window->source; uint32_t targetCX; uint32_t targetCY; int x, y; int newCX, newCY; float scale; if (source) { targetCX = std::max(obs_source_get_width(source), 1u); targetCY = std::max(obs_source_get_height(source), 1u); } else { struct obs_video_info ovi; obs_get_video_info(&ovi); targetCX = ovi.base_width; targetCY = ovi.base_height; } GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale); newCX = int(scale * float(targetCX)); newCY = int(scale * float(targetCY)); startRegion(x, y, newCX, newCY, 0.0f, float(targetCX), 0.0f, float(targetCY)); if (window->type == ProjectorType::Preview && main->IsPreviewProgramMode()) { OBSSource curSource = main->GetCurrentSceneSource(); if (source != curSource) { obs_source_dec_showing(source); obs_source_inc_showing(curSource); source = curSource; window->source = source; } } if (source) obs_source_video_render(source); else obs_render_main_texture(); endRegion(); }
/** Takes an OBS source and generates a JSON encoded string representing information about the source. @param source is the OBS source that we want to turn into json data @return json encoded string */ const char* obsSourceToJSON(obs_source_t *source) { const char *name = obs_source_get_name(source); json_t *obj = json_object(); json_object_set_new(obj, "name", json_string(name)); json_object_set_new(obj, "width", json_integer(obs_source_get_width(source))); json_object_set_new(obj, "height", json_integer(obs_source_get_height(source))); const char *jsonString = json_dumps(obj, 0); free(obj); return jsonString; }
static inline uint32_t labelOffset(obs_source_t *label, uint32_t cx) { uint32_t w = obs_source_get_width(label); int n; // Number of scenes per row switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: n = 6; break; default: n = 4; break; } w = uint32_t(w * ((1.0f) / n)); return (cx / 2) - w; }
static void recalculate_transition_size(obs_source_t *transition) { uint32_t cx = 0, cy = 0; obs_source_t *child; lock_transition(transition); for (size_t i = 0; i < 2; i++) { child = transition->transition_sources[i]; if (child) { uint32_t new_cx = obs_source_get_width(child); uint32_t new_cy = obs_source_get_height(child); if (new_cx > cx) cx = new_cx; if (new_cy > cy) cy = new_cy; } } unlock_transition(transition); transition->transition_actual_cx = cx; transition->transition_actual_cy = cy; }
static void sharpness_render(void *data, gs_effect_t *effect) { struct sharpness_data *filter = data; if (!filter) return; if (!obs_filter_get_target(filter->context)) return; obs_source_process_filter_begin(filter->context, GS_RGBA, OBS_ALLOW_DIRECT_RENDERING); filter->texwidth =(float)obs_source_get_width( obs_filter_get_target(filter->context)); filter->texheight = (float)obs_source_get_height( obs_filter_get_target(filter->context)); gs_effect_set_float(filter->sharpness_param, filter->sharpness); gs_effect_set_float(filter->texture_width, filter->texwidth); gs_effect_set_float(filter->texture_height, filter->texheight); obs_source_process_filter_end(filter->context, filter->effect, 0, 0); UNUSED_PARAMETER(effect); }
void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) { OBSProjector *window = reinterpret_cast<OBSProjector*>(data); uint32_t targetCX; uint32_t targetCY; int x, y; int newCX, newCY; float scale; if (window->source) { targetCX = std::max(obs_source_get_width(window->source), 1u); targetCY = std::max(obs_source_get_height(window->source), 1u); } else { struct obs_video_info ovi; obs_get_video_info(&ovi); targetCX = ovi.base_width; targetCY = ovi.base_height; } GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale); newCX = int(scale * float(targetCX)); newCY = int(scale * float(targetCY)); gs_viewport_push(); gs_projection_push(); gs_ortho(0.0f, float(targetCX), 0.0f, float(targetCY), -100.0f, 100.0f); gs_set_viewport(x, y, newCX, newCY); if (window->source) obs_source_video_render(window->source); else obs_render_main_view(); gs_projection_pop(); gs_viewport_pop(); }
static inline void render_item(struct obs_scene_item *item) { if (item->item_render) { uint32_t width = obs_source_get_width(item->source); uint32_t height = obs_source_get_height(item->source); uint32_t cx = calc_cx(item, width); uint32_t cy = calc_cy(item, height); if (cx && cy && gs_texrender_begin(item->item_render, cx, cy)) { float cx_scale = (float)width / (float)cx; float cy_scale = (float)height / (float)cy; struct vec4 clear_color; vec4_zero(&clear_color); gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0); gs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f); gs_matrix_scale3f(cx_scale, cy_scale, 1.0f); gs_matrix_translate3f( -(float)item->crop.left, -(float)item->crop.top, 0.0f); obs_source_video_render(item->source); gs_texrender_end(item->item_render); } } gs_matrix_push(); gs_matrix_mul(&item->draw_transform); if (item->item_render) { render_item_texture(item); } else { obs_source_video_render(item->source); } gs_matrix_pop(); }
void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) { OBSProjector *window = (OBSProjector *)data; if (updatingMultiview || !window->ready) return; OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window(); uint32_t targetCX, targetCY; int x, y; float scale; targetCX = (uint32_t)window->fw; targetCY = (uint32_t)window->fh; GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale); OBSSource previewSrc = main->GetCurrentSceneSource(); OBSSource programSrc = main->GetProgramSource(); bool studioMode = main->IsPreviewProgramMode(); auto renderVB = [&](gs_vertbuffer_t *vb, int cx, int cy, uint32_t colorVal) { if (!vb) return; matrix4 transform; matrix4_identity(&transform); transform.x.x = cx; transform.y.y = cy; gs_load_vertexbuffer(vb); gs_matrix_push(); gs_matrix_mul(&transform); gs_effect_set_color(window->color, colorVal); while (gs_effect_loop(window->solid, "Solid")) gs_draw(GS_LINESTRIP, 0, 0); gs_matrix_pop(); }; auto drawBox = [&](float cx, float cy, uint32_t colorVal) { gs_effect_set_color(window->color, colorVal); while (gs_effect_loop(window->solid, "Solid")) gs_draw_sprite(nullptr, 0, (uint32_t)cx, (uint32_t)cy); }; auto setRegion = [&](float bx, float by, float cx, float cy) { float vX = int(x + bx * scale); float vY = int(y + by * scale); float vCX = int(cx * scale); float vCY = int(cy * scale); float oL = bx; float oT = by; float oR = (bx + cx); float oB = (by + cy); startRegion(vX, vY, vCX, vCY, oL, oR, oT, oB); }; auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2 ) * window->scenesCY; if (i % 2 != 0) window->sourceX += window->scenesCX; break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2 ) * window->scenesCY; if (i % 2 != 0) window->sourceX = window->scenesCX; break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = window->pvwprgCY; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->pvwprgCY + window->scenesCY; } } window->siX = window->sourceX + window->thickness; window->siY = window->sourceY + window->thickness; }; auto calcPreviewProgram = [&](bool program) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = window->thickness + window->pvwprgCX / 2; window->sourceY = window->thickness; window->labelX = window->offset + window->pvwprgCX / 2; window->labelY = window->pvwprgCY * 0.85f; if (program) { window->sourceX += window->pvwprgCX; window->labelX += window->pvwprgCX; } break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->thickness; window->sourceY = window->pvwprgCY + window->thickness; window->labelX = window->offset; window->labelY = window->pvwprgCY * 1.85f; if (program) { window->sourceY = window->thickness; window->labelY = window->pvwprgCY * 0.85f; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = window->pvwprgCX + window->thickness; window->sourceY = window->pvwprgCY + window->thickness; window->labelX = window->pvwprgCX + window->offset; window->labelY = window->pvwprgCY * 1.85f; if (program) { window->sourceY = window->thickness; window->labelY = window->pvwprgCY * 0.85f; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: window->sourceX = window->thickness; window->sourceY = window->pvwprgCY + window->thickness; window->labelX = window->offset; window->labelY = window->pvwprgCY * 1.85f; if (program) { window->sourceX += window->pvwprgCX; window->labelX += window->pvwprgCX; } break; default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES: window->sourceX = window->thickness; window->sourceY = window->thickness; window->labelX = window->offset; window->labelY = window->pvwprgCY * 0.85f; if (program) { window->sourceX += window->pvwprgCX; window->labelX += window->pvwprgCX; } } }; auto paintAreaWithColor = [&](float tx, float ty, float cx, float cy, uint32_t color) { gs_matrix_push(); gs_matrix_translate3f(tx, ty, 0.0f); drawBox(cx, cy, color); gs_matrix_pop(); }; // Define the whole usable region for the multiview startRegion(x, y, targetCX * scale, targetCY * scale, 0.0f, window->fw, 0.0f, window->fh); // Change the background color to highlight all sources drawBox(window->fw, window->fh, outerColor); /* ----------------------------- */ /* draw sources */ for (size_t i = 0; i < maxSrcs; i++) { // Handle all the offsets calcBaseSource(i); if (i >= numSrcs) { // Just paint the background and continue paintAreaWithColor(window->sourceX, window->sourceY, window->scenesCX, window->scenesCY, outerColor); paintAreaWithColor(window->siX, window->siY, window->siCX, window->siCY, backgroundColor); continue; } OBSSource src = OBSGetStrongRef(window->multiviewScenes[i]); // We have a source. Now chose the proper highlight color uint32_t colorVal = outerColor; if (src == programSrc) colorVal = programColor; else if (src == previewSrc) colorVal = studioMode ? previewColor : programColor; // Paint the background paintAreaWithColor(window->sourceX, window->sourceY, window->scenesCX, window->scenesCY, colorVal); paintAreaWithColor(window->siX, window->siY, window->siCX, window->siCY, backgroundColor); /* ----------- */ // Render the source gs_matrix_push(); gs_matrix_translate3f(window->siX, window->siY, 0.0f); gs_matrix_scale3f(window->siScaleX, window->siScaleY, 1.0f); setRegion(window->siX, window->siY, window->siCX, window->siCY); obs_source_video_render(src); endRegion(); gs_matrix_pop(); /* ----------- */ // Render the label if (!drawLabel) continue; obs_source *label = window->multiviewLabels[i + 2]; if (!label) continue; window->offset = labelOffset(label, window->scenesCX); gs_matrix_push(); gs_matrix_translate3f(window->sourceX + window->offset, (window->scenesCY * 0.85f) + window->sourceY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); drawBox(obs_source_get_width(label), obs_source_get_height(label) + int(window->sourceY * 0.015f), labelColor); obs_source_video_render(label); gs_matrix_pop(); } /* ----------------------------- */ /* draw preview */ obs_source_t *previewLabel = window->multiviewLabels[0]; window->offset = labelOffset(previewLabel, window->pvwprgCX); calcPreviewProgram(false); // Paint the background paintAreaWithColor(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY, backgroundColor); // Scale and Draw the preview gs_matrix_push(); gs_matrix_translate3f(window->sourceX, window->sourceY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); setRegion(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY); if (studioMode) obs_source_video_render(previewSrc); else obs_render_main_texture(); if (drawSafeArea) { renderVB(window->actionSafeMargin, targetCX, targetCY, outerColor); renderVB(window->graphicsSafeMargin, targetCX, targetCY, outerColor); renderVB(window->fourByThreeSafeMargin, targetCX, targetCY, outerColor); renderVB(window->leftLine, targetCX, targetCY, outerColor); renderVB(window->topLine, targetCX, targetCY, outerColor); renderVB(window->rightLine, targetCX, targetCY, outerColor); } endRegion(); gs_matrix_pop(); /* ----------- */ // Draw the Label if (drawLabel) { gs_matrix_push(); gs_matrix_translate3f(window->labelX, window->labelY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); drawBox(obs_source_get_width(previewLabel), obs_source_get_height(previewLabel) + int(window->pvwprgCX * 0.015f), labelColor); obs_source_video_render(previewLabel); gs_matrix_pop(); } /* ----------------------------- */ /* draw program */ obs_source_t *programLabel = window->multiviewLabels[1]; window->offset = labelOffset(programLabel, window->pvwprgCX); calcPreviewProgram(true); paintAreaWithColor(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY, backgroundColor); // Scale and Draw the program gs_matrix_push(); gs_matrix_translate3f(window->sourceX, window->sourceY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); setRegion(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY); obs_render_main_texture(); endRegion(); gs_matrix_pop(); /* ----------- */ // Draw the Label if (drawLabel) { gs_matrix_push(); gs_matrix_translate3f(window->labelX, window->labelY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); drawBox(obs_source_get_width(programLabel), obs_source_get_height(programLabel) + int(window->pvwprgCX * 0.015f), labelColor); obs_source_video_render(programLabel); gs_matrix_pop(); } // Region for future usage with aditional info. if (multiviewLayout == MultiviewLayout::HORIZONTAL_TOP_24_SCENES) { // Just paint the background for now paintAreaWithColor(window->thickness, window->thickness, window->siCX, window->siCY * 2 + window->thicknessx2, backgroundColor); paintAreaWithColor(window->thickness + 2.5 * ( window->thicknessx2 + window->ppiCX), window->thickness, window->siCX, window->siCY * 2 + window->thicknessx2, backgroundColor); } endRegion(); }
static void update_item_transform(struct obs_scene_item *item) { uint32_t width = obs_source_get_width(item->source); uint32_t height = obs_source_get_height(item->source); uint32_t cx = calc_cx(item, width); uint32_t cy = calc_cy(item, height); struct vec2 base_origin; struct vec2 origin; struct vec2 scale = item->scale; struct calldata params; uint8_t stack[128]; if (os_atomic_load_long(&item->defer_update) > 0) return; width = cx; height = cy; vec2_zero(&base_origin); vec2_zero(&origin); /* ----------------------- */ if (item->bounds_type != OBS_BOUNDS_NONE) { calculate_bounds_data(item, &origin, &scale, &cx, &cy); } else { cx = (uint32_t)((float)cx * scale.x); cy = (uint32_t)((float)cy * scale.y); } add_alignment(&origin, item->align, (int)cx, (int)cy); matrix4_identity(&item->draw_transform); matrix4_scale3f(&item->draw_transform, &item->draw_transform, scale.x, scale.y, 1.0f); matrix4_translate3f(&item->draw_transform, &item->draw_transform, -origin.x, -origin.y, 0.0f); matrix4_rotate_aa4f(&item->draw_transform, &item->draw_transform, 0.0f, 0.0f, 1.0f, RAD(item->rot)); matrix4_translate3f(&item->draw_transform, &item->draw_transform, item->pos.x, item->pos.y, 0.0f); item->output_scale = scale; /* ----------------------- */ if (item->bounds_type != OBS_BOUNDS_NONE) { vec2_copy(&scale, &item->bounds); } else { scale.x = (float)width * item->scale.x; scale.y = (float)height * item->scale.y; } add_alignment(&base_origin, item->align, (int)scale.x, (int)scale.y); matrix4_identity(&item->box_transform); matrix4_scale3f(&item->box_transform, &item->box_transform, scale.x, scale.y, 1.0f); matrix4_translate3f(&item->box_transform, &item->box_transform, -base_origin.x, -base_origin.y, 0.0f); matrix4_rotate_aa4f(&item->box_transform, &item->box_transform, 0.0f, 0.0f, 1.0f, RAD(item->rot)); matrix4_translate3f(&item->box_transform, &item->box_transform, item->pos.x, item->pos.y, 0.0f); /* ----------------------- */ item->last_width = width; item->last_height = height; calldata_init_fixed(¶ms, stack, sizeof(stack)); calldata_set_ptr(¶ms, "scene", item->parent); calldata_set_ptr(¶ms, "item", item); signal_handler_signal(item->parent->source->context.signals, "item_transform", ¶ms); }
static void recalculate_transition_matrix(obs_source_t *tr, size_t idx) { obs_source_t *child; struct matrix4 mat; struct vec2 pos; struct vec2 scale; float tr_cx = (float)tr->transition_actual_cx; float tr_cy = (float)tr->transition_actual_cy; float source_cx; float source_cy; float tr_aspect = tr_cx / tr_cy; float source_aspect; enum obs_transition_scale_type scale_type = tr->transition_scale_type; lock_transition(tr); child = tr->transition_sources[idx]; if (!child) { unlock_transition(tr); return; } source_cx = (float)obs_source_get_width(child); source_cy = (float)obs_source_get_height(child); unlock_transition(tr); if (source_cx == 0.0f || source_cy == 0.0f) return; source_aspect = source_cx / source_cy; if (scale_type == OBS_TRANSITION_SCALE_MAX_ONLY) { if (source_cx > tr_cx || source_cy > tr_cy) { scale_type = OBS_TRANSITION_SCALE_ASPECT; } else { scale.x = 1.0f; scale.y = 1.0f; } } if (scale_type == OBS_TRANSITION_SCALE_ASPECT) { bool use_width = tr_aspect < source_aspect; scale.x = scale.y = use_width ? tr_cx / source_cx : tr_cy / source_cy; } else if (scale_type == OBS_TRANSITION_SCALE_STRETCH) { scale.x = tr_cx / source_cx; scale.y = tr_cy / source_cy; } source_cx *= scale.x; source_cy *= scale.y; vec2_zero(&pos); add_alignment(&pos, tr->transition_alignment, (int)(tr_cx - source_cx), (int)(tr_cy - source_cy)); matrix4_identity(&mat); matrix4_scale3f(&mat, &mat, scale.x, scale.y, 1.0f); matrix4_translate3f(&mat, &mat, pos.x, pos.y, 0.0f); matrix4_copy(&tr->transition_matrices[idx], &mat); }